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 <noreply@anthropic.com>
This commit is contained in:
njohnson 2026-01-11 12:12:00 -05:00
parent 8277be6518
commit 1b3b5ee194
3 changed files with 185 additions and 37 deletions

1
.gitignore vendored
View File

@ -47,4 +47,3 @@ tools/steamcmd/
# Environment files (API keys)
.env
app/.env

185
CLAUDE.md Normal file
View File

@ -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`)

View File

@ -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