Introduce operation journaling to track how values are read from the binary stream, enabling future write-back of edited fields. New components: - OperationJournal: Records read operations with stream offsets, sizes, byte order, and original values during parsing - ReverseEval: Analyzes expressions for invertibility (e.g., x+5 can be reversed to compute the source field when editing the result) - Recompiler: Infrastructure for recompiling modified values back to binary format Interpreter enhancements: - Add runTypeInternalWithJournal() for journaled parsing - Journal scalar reads, byte reads, strings, skip/seek operations - Add deadrising_lzx() built-in for Dead Rising 2 LZX decompression - Support _deadrising_lzx_stem context variable for nested archives Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
106 lines
3.6 KiB
C++
106 lines
3.6 KiB
C++
#ifndef REVERSEEVAL_H
|
|
#define REVERSEEVAL_H
|
|
|
|
#include "operationjournal.h"
|
|
#include "ast.h"
|
|
#include <QVariant>
|
|
|
|
/**
|
|
* @brief Analyzes expressions to determine invertibility and compute reverse operations.
|
|
*
|
|
* This is used during parsing to build InverseChain structures that enable
|
|
* editing computed fields. When a user edits a computed field, the inverse
|
|
* chain is used to calculate what the source field's value should be.
|
|
*
|
|
* Invertible operations (single source):
|
|
* - Addition: x = y + c → y = x - c
|
|
* - Subtraction: x = y - c → y = x + c
|
|
* - Multiplication: x = y * c → y = x / c
|
|
* - Division: x = y / c → y = x * c
|
|
* - Left shift: x = y << c → y = x >> c
|
|
* - Right shift: x = y >> c → y = x << c
|
|
* - Negation: x = -y → y = -x
|
|
* - Bitwise XOR: x = y ^ c → y = x ^ c (XOR is self-inverse)
|
|
*
|
|
* Non-invertible operations (information loss):
|
|
* - Bitwise AND: x = y & c (bits masked out are lost)
|
|
* - Bitwise OR: x = y | c (can't determine which bits were originally set)
|
|
* - Modulo: x = y % c (infinite solutions)
|
|
* - Multiple sources: x = a + b (can't determine which to adjust)
|
|
*/
|
|
class ReverseEvaluator {
|
|
public:
|
|
ReverseEvaluator() = default;
|
|
|
|
/**
|
|
* @brief Analyze an expression to build an inverse chain.
|
|
* @param expr The expression to analyze
|
|
* @param targetField The field name being assigned to (for context)
|
|
* @return InverseChain with source field and operations, or canInvert=false if not invertible
|
|
*/
|
|
InverseChain analyzeExpression(const Expr& expr, const QString& targetField = QString());
|
|
|
|
/**
|
|
* @brief Apply an inverse chain to compute the source value from an edited value.
|
|
* @param chain The inverse chain from analyzeExpression
|
|
* @param editedValue The new value the user entered
|
|
* @return The computed source value
|
|
*/
|
|
qint64 computeInverse(const InverseChain& chain, qint64 editedValue) const;
|
|
|
|
/**
|
|
* @brief Apply inverse chain with QVariant (delegates to qint64 version).
|
|
*/
|
|
QVariant computeInverse(const InverseChain& chain, const QVariant& editedValue) const;
|
|
|
|
/**
|
|
* @brief Quick check if an expression is invertible.
|
|
* @param expr The expression to check
|
|
* @return true if the expression can be reversed to a single source field
|
|
*/
|
|
bool isInvertible(const Expr& expr) const;
|
|
|
|
/**
|
|
* @brief Get human-readable reason why an expression is not invertible.
|
|
* @param expr The expression to check
|
|
* @return Empty string if invertible, otherwise the reason
|
|
*/
|
|
QString whyNotInvertible(const Expr& expr) const;
|
|
|
|
private:
|
|
/**
|
|
* @brief Recursively analyze an expression, building up the inverse chain.
|
|
* @param expr The expression to analyze
|
|
* @param chain The chain being built (modified in place)
|
|
* @return true if analysis succeeded and expression is invertible
|
|
*/
|
|
bool analyzeRecursive(const Expr& expr, InverseChain& chain);
|
|
|
|
/**
|
|
* @brief Check if an expression is a constant (literal value).
|
|
*/
|
|
bool isConstant(const Expr& expr) const;
|
|
|
|
/**
|
|
* @brief Get the constant value from an expression.
|
|
*/
|
|
qint64 getConstantValue(const Expr& expr) const;
|
|
|
|
/**
|
|
* @brief Check if an expression is a simple variable reference.
|
|
*/
|
|
bool isVariable(const Expr& expr) const;
|
|
|
|
/**
|
|
* @brief Get the variable name from an expression.
|
|
*/
|
|
QString getVariableName(const Expr& expr) const;
|
|
|
|
/**
|
|
* @brief Apply a single inverse operation.
|
|
*/
|
|
qint64 applyInverseOp(qint64 value, InvertOp op, qint64 operand) const;
|
|
};
|
|
|
|
#endif // REVERSEEVAL_H
|