From 1b3b5ee1943322ea654e8300c6ecad80b40353a3 Mon Sep 17 00:00:00 2001 From: njohnson Date: Sun, 11 Jan 2026 12:12:00 -0500 Subject: [PATCH] Add project documentation and clean up obsolete files - Add CLAUDE.md with project overview and XScript reference - Remove obsolete ai-commit.sh script - Clean up duplicate .env entry in .gitignore Co-Authored-By: Claude Opus 4.5 --- .gitignore | 1 - CLAUDE.md | 185 +++++++++++++++++++++++++++++++++++++++++++++++++++ ai-commit.sh | 36 ---------- 3 files changed, 185 insertions(+), 37 deletions(-) create mode 100644 CLAUDE.md delete mode 100644 ai-commit.sh diff --git a/.gitignore b/.gitignore index 4cfbfad..f650e0b 100644 --- a/.gitignore +++ b/.gitignore @@ -47,4 +47,3 @@ tools/steamcmd/ # Environment files (API keys) .env -app/.env diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..64da217 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,185 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Build Instructions + +**DO NOT BUILD** - The user will build manually. This project uses Qt 6.10.0 with MSVC 2022 on Windows. + +Build commands (for reference only): +```bash +qmake XPlor.pro +nmake # or jom on Windows +``` + +## Project Overview + +XPlor is a Qt-based binary file format explorer for video game files. It uses **XScript**, a custom DSL for defining binary file structures, allowing new formats to be added without modifying core code. + +## Architecture + +``` +XPlor/ +├── app/ # Qt GUI application (MainWindow, previews, settings) +├── libs/ +│ ├── dsl/ # XScript engine (lexer, parser, interpreter, typeregistry) +│ ├── core/ # Utilities, logging, syntax highlighters +│ ├── compression/ # LZO, zlib, Oodle, xcompress64 support +│ └── encryption/ # Salsa20, SHA1 support +├── definitions/ # XScript format definitions +│ ├── cod/ # Call of Duty formats (60+ files) +│ ├── asura/ # Sniper Elite V2 formats +│ └── [other engines] +└── third_party/ # External SDKs (DevIL, Xbox SDK, zlib) +``` + +## DSL Engine Flow + +1. `TypeRegistry` loads and merges `.xscript` files from `definitions/` +2. `Lexer` tokenizes XScript source +3. `Parser` builds AST +4. `Interpreter` executes AST against binary streams +5. Results stored as `QVariantMap` for UI display + +## XScript Language Reference + +### Type Definition +```xscript +type typename [root, display="Display Name"] byteorder LE +{ + criteria { require _ext == "ff"; } + + // Parse body + magic = ascii(read(4)); + version = u32; +} +``` + +### Scalar Types +`u8`, `i8`, `u16`, `i16`, `u32`, `i32`, `u64`, `i64`, `f32`, `f64`, `bool` + +### Built-in Functions + +**Reading:** +- `read(n)` - read n bytes +- `cstring()` - read null-terminated string +- `wstring()` / `wstring_be()` - read UTF-16 LE/BE string +- `skip(n)` - skip n bytes +- `align(n)` - align to n-byte boundary +- `seek(pos)` - seek to absolute position +- `pos()` / `size()` - current position / file size + +**Random Access:** +- `u8at(off)`, `u16at(off)`, `u32at(off)`, `u64at(off)`, `i32at(off)` - read at offset without advancing +- `bytesat(off, len)` - read bytes at offset +- `with_seek(offset, expr)` - evaluate expression at offset, restore position + +**Parsing:** +- `parse("typename", bytes)` - parse bytes as type (creates new stream) +- `parse_here("typename")` - parse type at current position (same stream, delegation pattern) + +**Context:** +- `ctx_set(name, value)` - set global context variable (cross-type communication) +- `ctx_get(name)` - get global context variable +- `set_name(value)` - set item name for tree display +- `set_display(value)` - set display name + +**Decompression:** +- `zlib(bytes)` - decompress zlib +- `xmem(bytes)` - decompress Xbox xcompress +- `deflate(bytes)` - decompress raw deflate + +**Conversion:** +- `ascii(bytes)` / `utf8(bytes)` / `utf16le(bytes)` / `utf16be(bytes)` - bytes to string +- `hex(value)` - format as hex string + +**Collections:** +- `make_list()` / `make_object()` - create empty list/object +- `push(listName, value)` - append to list +- `get(container, key)` - get from list/object +- `set(varName, key, value)` - set in list/object +- `len(value)` - length of string/bytes/list + +**String:** +- `contains(str, sub)`, `starts_with(str, prefix)`, `ends_with(str, suffix)` +- `substr(str, start, len)`, `split(str, delim)`, `replace(str, old, new)` +- `trim(str)`, `to_lower(str)`, `clean(str)`, `basename(path)` + +**UI Metadata:** +- `ui(varName, displayName)` - mark field for readonly display +- `ui_edit(varName, displayName)` - mark field for editable display +- `hide(varName)` - hide from UI +- `set_viewer("hex"|"text"|"image"|"audio")` - set viewer type +- `preview(data)` / `set_preview(filename, data)` - set preview data + +**Control:** +- `match(value, case1, result1, case2, result2, ..., default)` - switch expression +- `bit(value, bit)` / `bit(value, start, count)` - extract bits +- `check_criteria("typename")` - test if type criteria matches current position + +### Control Flow +```xscript +if (condition) { ... } else { ... } +while (condition) { ... } +repeat count { ... } // provides _i index +for i in start..end { ... } +break; + +// Match expression (replaces long if-else chains) +match(value) { + "pattern1" => { ... } + "pattern2" | "pattern3" => { ... } // multiple patterns + default => { ... } +} +``` + +### Constants +```xscript +const PTR_INLINE = -1; +const MAGIC_HEADER = 0x12345678; +``` + +### Inline Declaration (simplifies pointer-to-inline patterns) +```xscript +// Instead of: if (name_ptr == -1) { name = cstring(); ui("name", "Name"); set_name(name); } +inline cstring name when name_ptr [ui="Name", set_name]; + +// For complex types: +inline material mat when mat_ptr; +inline itemkeyhandler handler when handler_ptr; +``` + +### Array Declaration (simplifies repeat/push patterns) +```xscript +// Instead of: items = 0; repeat(count) { _x = parse_here("type"); items = push("items", _x); } +array[count] items: typename; + +// With pointer condition: +array[count] items: typename when items_ptr; +``` + +### Pipeline Syntax +```xscript +zone = read(size) | zlib | parse("zone"); +``` + +### File Variables +- `_path` - full file path +- `_name` - filename +- `_basename` - filename without extension +- `_ext` - file extension (lowercase) + +## Key Files + +- `libs/dsl/interpreter.cpp` - Runtime execution, all built-in functions +- `libs/dsl/parser.cpp` - AST generation +- `libs/dsl/typeregistry.cpp` - Type loading and file detection +- `app/mainwindow.cpp` - Main UI logic +- `definitions/cod/fastfile.xscript` - Primary COD format (good example) + +## Conventions + +- Use `ctx_set()`/`ctx_get()` for cross-type context (not `set_global`/`get_global`) +- Use `skip(n)` for padding, not `_padding = read(n)` +- Pointer values of `-1` indicate inline data follows +- Use `-1` consistently (not `4294967295` or `0xFFFFFFFF`) diff --git a/ai-commit.sh b/ai-commit.sh deleted file mode 100644 index de7a0dc..0000000 --- a/ai-commit.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash -set -e - -# 1. Stage everything -git add -A - -# 2. Get list of staged files -FILES=$(git diff --cached --name-only) - -if [ -z "$FILES" ]; then - echo "No changes to commit." - exit 0 -fi - -# 3. Loop file by file -for FILE in $FILES; do - # Get diff for this file - DIFF=$(git diff --cached -- "$FILE") - - if [ -z "$DIFF" ]; then - continue - fi - - # Ask Ollama for a commit message describing this file change - MSG=$(echo "$DIFF" | ollama run gemma3 \ - "You are a commit bot. Write a SHORT, clear, concise Git commit message for changes in file: $FILE. -Only output the commit message, nothing else. -Diff: -$DIFF") - - # Commit just this file with its message - git commit -m "$MSG" -- "$FILE" - - echo "✅ Committed $FILE with message:" - echo "$MSG" -done