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:
parent
8277be6518
commit
1b3b5ee194
1
.gitignore
vendored
1
.gitignore
vendored
@ -47,4 +47,3 @@ tools/steamcmd/
|
|||||||
|
|
||||||
# Environment files (API keys)
|
# Environment files (API keys)
|
||||||
.env
|
.env
|
||||||
app/.env
|
|
||||||
|
|||||||
185
CLAUDE.md
Normal file
185
CLAUDE.md
Normal 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`)
|
||||||
36
ai-commit.sh
36
ai-commit.sh
@ -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
|
|
||||||
Loading…
x
Reference in New Issue
Block a user