Compare commits

...

13 Commits

Author SHA1 Message Date
Stéphane Bisinger
a534e2a23b Add missing #pragma once to includes (#494) 2026-02-07 12:58:31 -07:00
MrClock
525f3ad4d6 patterns/magic: Add more Arma 3 files (#479)
* patterns/texheaders: Added pattern for Arma 3 texHeaders.bin

* magic/arma3: Added texHeaders.bin magic

* patterns/texheaders: Added test file

* patterns/paa: Small improvements

Added extra data description, and better indication of internally compressed data mipmap data.

* patterns/a3: Moved Arma 3 patterns into common folder

* patterns/a3: Added pattern for MLOD P3D

* patterns/a3: Added pattern for RAP

* magic/arma3: Added P3D and RAP to magic file

* patterns/a3: Added test files for P3D and RAP

* patterns/a3: Small correction to type names in TexHeaders format
2026-02-04 07:35:57 +01:00
gmestanley
27480b1da9 encodings: Removing control characters from TBLs (#481)
* Removing control characters from TBLs

* Delete patterns/gbk.tbl

* Delete patterns/jis_x_0211.tbl

* Delete patterns/euc_jp.tbl

* Add files via upload
2026-01-20 08:05:01 +01:00
MrClock
6a2a963a5a patterns: Add pattern and magic for Arma 3 RTM (#478)
* patterns/rtm: Added pattern for Arma 3 plain RTM

* magic/arma3: Added plain RTM to magic file

* patterns/rtm: Added test file

* patterns/rtm: Changed function parameters to refs

* patterns/rtm: Replaced RTM test file

* patterns/rtm: Added extra description to RTM pattern

* pattern/bmtr: Added BMTR pattern, magic and test file
2026-01-13 07:10:43 +01:00
MrClock
cee3a5de77 patterns: Add pattern and magic for Arma 3 PAA (#475)
* patterns/paa: Added pattern for Arma 3 PAA

* patterns/paa: Small correction

* magic/arma3: Added magic file for Arma 3 formats

Only the PAA format for now
2026-01-11 19:10:23 +01:00
gmestanley
fb84bbb5d1 patterns: More accurate variable declarations on nes.hexpat, add n64, gen, and gbx.hexpat (#477)
* Add n64, gen, and gbx.hexpat

* Add n64, gbx, and gen.hexpat to README.md

* Remove leftover string import from n64.hexpat

* More accurate variable declarations on nes.hexpat

* Add source to gbx.hexpat

* Add accidentally missing curly brace in nes.hexpat
2026-01-11 18:59:37 +01:00
Dexrn ZacAttack
097ab49cae patterns: Add SPC pattern (#476)
* feat: SPC700 hexpat

* chore: add pattern to readme
2026-01-11 09:53:20 +01:00
Illia Trukhanov
5a04a4256b pattern: Add Lua 5.0 pattern (#466)
* patterns: Add Lua 5.0 pattern

Based on Lua 5.1 pattern.

* tests: Add Lua 5.0 test file

function fac(n)
    if n < 2 then
        return 1
    end
    return n * fac(n - 1)
end

./luac - < test.lua
Lua compiled with make ARFLAGS=rv CC="gcc -m32"
at commit 762c7370376dbd13cd8aeb4d8c8da0bb153269c3
luac compiled with make LUA=../lua CC="gcc -m32"
at commit d002063c4605d3ea12d419b34a23c562f9add318

---------

Co-authored-by: Nik <werwolv98@gmail.com>
2026-01-03 20:31:40 +01:00
gmestanley
8a9bd577ad patterns: Fix quirks of nes.hexpat with famiclones and miscellaneous fixes (#467)
* Add credit to ne.hexpat

* Add many changes to nes.hexpat

* Fixing dependance on variables declared in if statement

* Added mappers and inline to NES 2.0 header, removed needless parenthesises

* Add files via upload

* Add files via upload

* Create nsf.hexpat

* Used full name of the SNES on description

* Add SNES, NSF & NSFe, new description for NES

* Removing erroneous condition in ips.hexpat's truncatedSize

* Removing unnecessary std.string import in ips.hexpat

* Added both locations for sections in PE, clearer variable names, reorganized DOS stub

* Delete patterns/nsfe.hexpat

* Delete patterns/nsfmetadata.hexpat

* Added chunks from NSFe to NSF

* Added NSFe

* Fix size of truncatedSize in ips.hexpat

* More mappers, deprecated tag, and constants to nes

* Fixed official header's constants

* Various corrections

* Fix on declaration of sections, reserved words made into padding

* Added hacked algorithm for longer PRG ROM size, fixed iNES check

* For nes.hexpat, fixed mapper 51 still having its submapper's name

* Fix bug that makes size negative on standard roms

---------

Co-authored-by: Nik <werwolv98@gmail.com>
2026-01-03 20:25:48 +01:00
Jarkko Pöyry
190f9d891b patterns: Add ASCII CPIO patterns. (#473) 2026-01-03 20:24:31 +01:00
Jack Manning
2d92bfca13 pattern: Add Chromium pak file (#474)
* Add Chromium pak file pattern

* Show aliases & fix brotli check bug

* Add decompressed size

* Add notes

* Add PakEncoding enum
2026-01-03 20:23:53 +01:00
elyashiv
0e4040fae1 patterns/elf: Added binding and symbol info (#471) 2025-12-30 11:24:19 +01:00
Nik
57c3dd44b8 tests: Fix test failures after library update 2025-12-23 23:26:20 +01:00
43 changed files with 2024 additions and 334 deletions

View File

@@ -36,6 +36,12 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi
| ARC | | [`patterns/arc.hexpat`](patterns/arc.hexpat) | Minecraft Legacy Console Edition ARC files |
| ARIA2 | | [`patterns/aria2.hexpat`](patterns/aria2.hexpat) | ARIA2 Download Manager Control files |
| ARM VTOR | | [`patterns/arm_cm_vtor.hexpat`](patterns/arm_cm_vtor.hexpat) | ARM Cortex M Vector Table Layout |
| Arma 3 config | `application/x.a3-rap` | [`patterns/a3/a3_rap.hexpat`](patterns/a3/a3_rap.hexpat) | Arma 3 binary/rapified config |
| Arma 3 P3D (MLOD) | `model/x.a3-p3d-mlod` | [`patterns/a3/a3_p3d_mlod.hexpat`](patterns/a3/a3_p3d_mlod.hexpat) | Arma 3 P3D model file (MLOD) |
| Arma 3 PAA | `image/x.a3-paa` | [`patterns/a3/a3_paa.hexpat`](patterns/a3/a3_paa.hexpat) | Arma 3 PAA texture file |
| Arma 3 RTM | `application/x.a3-rtm` | [`patterns/a3/a3_rtm.hexpat`](patterns/a3/a3_rtm.hexpat) | Arma 3 RTM animation file (plain) |
| Arma 3 RTM (binarized) | `application/x.a3-bmtr` | [`patterns/a3/a3_bmtr.hexpat`](patterns/a3/a3_bmtr.hexpat) | Arma 3 RTM animation file (binarized) |
| Arma 3 texHeaders.bin | `application/x.a3-texheaders` | [`patterns/a3/a3_texheaders.hexpat`](patterns/a3/a3_texheaders.hexpat) | Arma 3 texture index file |
| Assassin's Creed: Unity | | [`patterns/AC Unity`](patterns/Assassin's Creed: Unity) | Assassin's Creed: Unity archive files -- .forge & .data (compressed and decompressed) -- |
| Bastion | | [`patterns/bastion/*`](https://gitlab.com/EvelynTSMG/imhex-bastion-pats) | Various [Bastion](https://en.wikipedia.org/wiki/Bastion_(video_game)) files |
| BeyondCompare BCSS | | [`patterns/bcss.hexpat`](patterns/bcss.hexpat) | BeyondCompare Snapshot (BCSS) file |
@@ -57,8 +63,11 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi
| CDA | | [`patterns/cda.hexpat`](patterns/cda.hexpat) | Compact Disc Audio track |
| CHD | | [`patterns/chd.hexpat`](patterns/chd.hexpat) | MAME Compressed Hunks of Data file |
| CHM | `application/vnd.ms-htmlhelp` | [`patterns/chm.hexpat`](patterns/chm.hexpat) | Windows HtmlHelp Data (ITSF / CHM) |
| Chromium Pak | | [`patterns/chromium_pak.hexpat`](patterns/chromium_pak.hexpat) | Chromium pak file |
| COFF | `application/x-coff` | [`patterns/coff.hexpat`](patterns/coff.hexpat) | Common Object File Format (COFF) executable |
| CPIO | `application/x-cpio` | [`patterns/cpio.hexpat`](patterns/cpio.hexpat) | Old Binary CPIO Format |
| CPIO | | [`patterns/cpio_portable_ascii.hexpat`](patterns/cpio_portable_ascii.hexpat) | Portable ASCII CPIO Format |
| CPIO | | [`patterns/cpio_new_ascii.hexpat`](patterns/cpio_new_ascii.hexpat) | "New" ASCII CPIO Format |
| CrashLvl | | [`patterns/Crashlvl.hexpat`](patterns/Crashlvl.hexpat) | Crash Bandicoot - Back in Time (fan game) User created level format |
| CREDHIST | | [`patterns/credhist.hexpat`](patterns/credhist.hexpat) | CREDHIST Format |
| DDS | `image/vnd-ms.dds` | [`patterns/dds.hexpat`](patterns/dds.hexpat) | DirectDraw Surface |
@@ -94,6 +103,8 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi
| Flipper Zero Settings | | [`patterns/flipper_settings.hexpat`](patterns/flipper_settings.hexpat) | Flipper Zero Settings Files |
| GB | `application/x-gameboy-rom` | [`patterns/gb.hexpat`](patterns/gb.hexpat) | Game Boy ROM |
| GBA | `application/x-gameboy-advance-rom` | [`patterns/gba.hexpat`](patterns/gba.hexpat) | Game Boy Advance ROM header |
| GBX | | [`patterns/gbx.hexpat`](patterns/gbx.hexpat) | GameBoy ROM file GBX footer |
| Gen | | [`patterns/gen.hexpat`](patterns/gen.hexpat) | Sega Genesis/MegaDrive ROM |
| GGUF | | [`patterns/gguf.hexpat`](patterns/gguf.hexpat) | GGML Inference Models |
| GIF | `image/gif` | [`patterns/gif.hexpat`](patterns/gif.hexpat) | GIF image files |
| GLTF | `model/gltf-binary` | [`patterns/gltf.hexpat`](patterns/gltf.hexpat) | GL Transmission Format binary 3D model file |
@@ -117,6 +128,7 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi
| LOC | | [`patterns/loc.hexpat`](patterns/loc.hexpat) | Minecraft Legacy Console Edition Language file |
| Lua 4.0 | | [`patterns/lua40.hexpat`](patterns/lua40.hexpat) | Lua 4.0 bytecode |
| LUC | | [`patterns/popcap_luc.hexpat`](patterns/popcap_luc.hexpat) | PopCap's proprietary Lua bytecode |
| Lua 5.0 | | [`patterns/lua50.hexpat`](patterns/lua50.hexpat) | Lua 5.0 bytecode |
| Lua 5.1 | | [`patterns/lua51.hexpat`](patterns/lua51.hexpat) | Lua 5.1 bytecode |
| Lua 5.2 | | [`patterns/lua52.hexpat`](patterns/lua52.hexpat) | Lua 5.2 bytecode |
| Lua 5.3 | | [`patterns/lua53.hexpat`](patterns/lua53.hexpat) | Lua 5.3 bytecode |
@@ -132,6 +144,7 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi
| MSSCMP | | [`patterns/msscmp.hexpat`](patterns/msscmp.hexpat) | Miles Sound System Compressed Archive |
| NACP | | [`patterns/nacp.hexpat`](patterns/nacp.hexpat) | Nintendo Switch NACP files |
| NBT | | [`patterns/nbt.hexpat`](patterns/nbt.hexpat) | Minecraft NBT format |
| N64 | | [`patterns/n64.hexpat`](patterns/n64.hexpat) | Nintendo 64 ROM header |
| NDS | `application/x-nintendo-ds-rom` | [`patterns/nds.hexpat`](patterns/nds.hexpat) | DS Cartridge Header |
| NE | `application/x-ms-ne-executable` | [`patterns/ne.hexpat`](patterns/ne.hexpat) | NE header and Standard NE fields |
| nes | | [`patterns/nes.hexpat`](patterns/nes.hexpat) | Nintendo Entertainment System ROM |
@@ -185,6 +198,7 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi
| smk | | [`patterns/smk.hexpat`](patterns/smk.hexpat) | Smacker video file |
| SNES | | [`patterns/snes.hexpat`](patterns/snes.hexpat) | Super Nintendo Entertainment System ROM header |
| sup | | [`patterns/sup.hexpat`](patterns/sup.hexpat) | PGS Subtitle |
| SPC | | [`patterns/spc.hexpat`](patterns/spc.hexpat) | Super Nintendo Entertainment System SPC-700 dump file |
| SPIRV | | [`patterns/spirv.hexpat`](patterns/spirv.hexpat) | SPIR-V header and instructions |
| STDF | | [`patterns/stdfv4.hexpat`](patterns/stdfv4.hexpat) | Standard test data format for IC testers |
| STL | `model/stl` | [`patterns/stl.hexpat`](patterns/stl.hexpat) | STL 3D Model format |
@@ -258,6 +272,7 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi
| Name | Path | Description |
|------|------|-------------|
| Arma 3 | [`magic/arma3_magic`](magic/arma3_magic) | Identifies Arma 3 binary formats |
| Nintendo Switch | [`magic/nintendo_switch_magic`](magic/nintendo_switch_magic) | Identifies common file types used on the Nintendo Switch |
| Portable Executable | [`magic/portable_executable_magic`](magic/portable_executable_magic) | Identifies PE files used on Windows

Binary file not shown.

View File

@@ -21884,38 +21884,4 @@ FE4B=﨣
FE4C=﨤
FE4D=﨧
FE4E=﨨
FE4F=﨩
00=
01=
02=
03=
04=
05=
06=
07=
08=
09=
0A=
0B=
0C=
0D=
0E=
0F=
10=
11=
12=
13=
14=
15=
16=
17=
18=
19=
1A=
1B=
1C=
1D=
1E=
1F=
FE4F=﨩

Binary file not shown.

View File

@@ -1,3 +1,5 @@
#pragma once
import std.io;
import std.core;
@@ -67,4 +69,4 @@ namespace auto std::attr {
struct Sealed {}
[[sealed]];
}
}

View File

@@ -1,3 +1,4 @@
#pragma once
import std.io;
import std.string;
import std.mem;

View File

@@ -1,3 +1,5 @@
#pragma once
import std.string;
import std.sys;
import std.io;
@@ -42,4 +44,4 @@ namespace auto type
return std::format("\"{}\"", type::escape_bytes(magic.value));
};
}
}
}

View File

@@ -1,3 +1,5 @@
#pragma once
import std.mem;
/*!

View File

@@ -1,3 +1,5 @@
#pragma once
import std.io;
/*!
@@ -69,4 +71,4 @@ namespace auto type {
}
}
}

41
magic/arma3_magic Normal file
View File

@@ -0,0 +1,41 @@
# A libmagic database containing definitions for files used by the Arma 3 game by Bohemia Interactive
# Arma 3 PAA image
0x02 string GGAT Arma 3 PAA image file
!:mime image/x.a3-paa
!:ext paa
>0 leshort 0xff01 DXT1 compression
>0 leshort 0xff02 DXT2 compression
>0 leshort 0xff03 DXT3 compression
>0 leshort 0xff04 DXT4 compression
>0 leshort 0xff05 DXT5 compression
>0 leshort 0x4444 RGBA4 format
>0 leshort 0x1555 RGBA5 format
>0 leshort 0x8888 RGBA8 format
>0 leshort 0x8080 Grayscale format
# Arma 3 RTM animation
0 string RTM_ Arma 3 RTM animation file (plain)
!:mime application/x.a3-rtm
!:ext rtm
# Arma 3 binarized RTM animation
0 string BMTR Arma 3 RTM animation file (binarized)
!:mime application/x.a3-bmtr
!:ext rtm
# Arma 3 texture index
0 string 0DHT Arma 3 texture index file
!:mime application/x.a3-texheaders
!:ext bin
# Arma 3 MLOD P3D model
0 string MLOD Arma 3 P3D model file (MLOD)
!:mime model/x.a3-p3d-mlod
!:ext p3d
>0x0c string P3DM P3DM LOD type
# Arma 3 binarized config
0x01 string raP Arma 3 binary configuration file
!:mime application/x.a3-rap
!:ext bin

129
patterns/a3/a3_bmtr.hexpat Normal file
View File

@@ -0,0 +1,129 @@
#pragma author MrClock
#pragma description Arma 3 RTM animation format (binarized)
#pragma endian little
#pragma MIME application/x.a3-bmtr
fn get_data_description() {
return "Binarized RTM (BMTR) animation files are the PBO packed versions of plain RTMs.\n Binarized files are optimized for use by the game engine, and they are not editable.\nBone transformations are stored as relative quaternion-vector pairs.\nData blocks are conditionally LZO1X compressed (these are not supported by this pattern).";
};
import std.string;
import std.sys;
import type.float16;
using asciiz = std::string::NullString [[format("formatAsciiz")]];
using half = type::float16 ;
struct s16float {
s16 data;
} [[sealed,static,transform("transforms16float"), format("transforms16float")]];
struct Property {
padding[4];
asciiz name;
float phase;
asciiz value;
} [[format("formatProperty")]];
struct Vector<DataType> {
DataType x [[comment("+Left/-Right")]];
DataType y [[comment("+Up/-Down (UNUSED)")]];
DataType z [[comment("+Forward/-Backward")]];
} [[static]];
struct Quaternion {
s16float x;
s16float y;
s16float z;
s16float w;
} [[static,format("formatQuaternion")]];
struct Transform {
Quaternion orientation;
Vector<half> position [[format("formatVectorHalf")]];
} [[static]];
struct Frame {
u32 count_bones;
bool compressed = count_bones * sizeof(Transform) >= 1024;
if (parent.version > 4) {
u8 lzo_flag;
compressed = lzo_flag > 0;
}
if (compressed) {
std::error("Transformations are LZO compressed and compressed length is unknown");
} else {
Transform transforms[count_bones];
}
};
struct BMTR {
char signature[4];
u32 version;
padding[1];
Vector<float> motion [[format("formatVectorFloat")]];
u32 count_frames;
padding[4];
u32 count_bones;
u32 count_bones_again;
std::assert_warn(count_bones == count_bones_again, "Mismatch between bone counts");
asciiz bones[count_bones];
if (version >= 4) {
padding[4];
u32 count_properties;
Property properties[count_properties];
}
u32 count_phases;
std::assert_warn(count_frames == count_phases, "Frame and phase counts do not match");
bool compressed = count_phases * sizeof(float) >= 1024;
if (version > 4) {
u8 lzo_flag;
compressed = lzo_flag > 0;
}
if (compressed) {
std::error("Phases are LZO compressed and compressed length is unknown");
} else {
float phases[count_phases];
}
Frame frames[count_frames];
};
fn transforms16float(ref s16float value) {
return float(value.data) / 16384;
};
fn formatAsciiz(ref asciiz value) {
return std::format("\"{:s}\"", value);
};
fn formatProperty(ref Property prop) {
return std::format("\"{0:s}\" = \"{1:s}\" @ {2:.4f}", prop.name, prop.value, prop.phase);
};
fn formatVectorHalf(ref Vector<half> vec) {
return std::format(
"[{0}, {1}, {2}]",
type::impl::format_float16(vec.x),
type::impl::format_float16(vec.y),
type::impl::format_float16(vec.z)
);
};
fn formatVectorFloat(ref Vector<float> vec) {
return std::format("[{0:.2f}, {1:.2f}, {2:.2f}]", vec.x, vec.y, vec.z);
};
fn formatQuaternion(ref Quaternion q) {
return std::format("[{0:.2f}, {1:.2f}, {2:.2f}, {3:.2f}]", q.x, q.y, q.z, q.w);
};
BMTR file @ 0x0000;

View File

@@ -0,0 +1,222 @@
#pragma author MrClock
#pragma description Arma 3 P3D model format (MLOD)
#pragma endian little
#pragma MIME model/x.a3-p3d-mlod
fn get_data_description() {
return "MLOD type P3D files are used for authoring 3D models for Arma 3.\nThese files can be carated in and edited in the Object Builder application.\nAll data is stored uncompressed for ease of editing.\nDuring the PBO packing process they are further \"binarized\" into ODOL type P3D files, that are optimized for use by the game engine. (These are no longer editable, the conversion is irreversible.)\n\nP3D model files can by quite large, so by default only the 1st LOD is processed by this pattern. Processing of all LODs can be enabled in the pattern settings.";
};
import std.string;
import std.core;
bool process_all_lods in;
using asciiz = std::string::NullString;
enum FaceType: u32 {
TRIANGLE = 3,
QUAD = 4
};
struct Vector {
float x;
float y;
float z;
} [[static,format("formatVector")]];
struct UV {
float u;
float v;
} [[static,format("formatUV")]];
enum SurfaceFitting: u8 {
NORMAL = 0,
ON_SURFACE = 1,
ABOVE_SURFACE = 2,
UNDER_SURFACE = 4,
KEEP_HEIGHT = 8
};
enum Lighting: u8 {
NORMAL = 0,
SHINING = 1,
SHADOWED = 2,
FULL_LIT = 4,
HALF_LIT = 8
};
enum DecalMode: u8 {
NORMAL = 0,
DECAL = 1,
RADIO12 = 2
};
enum Fog: u8 {
NORMAL = 0,
NONE = 1,
SKY = 2
};
enum NormalCalculation: u8 {
FACE_AREA = 0,
HIDDE_VERTEX = 1,
FIXED = 2,
FACE_ANLGE = 4
};
bitfield VertexFlags {
padding : 5;
NormalCalculation normals : 3;
u8 user;
padding : 2;
Fog fog : 2;
padding : 2;
DecalMode decal : 2;
Lighting lighting : 4;
SurfaceFitting surface : 4;
} [[bitfield_order(std::core::BitfieldOrder::MostToLeastSignificant, 32)]];
struct Vertex {
Vector position;
VertexFlags flags;
}[[static,format("formatVertex")]];
struct FacePoint {
u32 vertex_index;
u32 normal_index;
UV uv;
} [[static]];
enum ZBiasFlag: u8 {
NONE = 0,
LOW = 1,
MIDDLE = 2,
HIGH = 3
};
bitfield FaceFlags {
user : 7;
disable_texture_merging : 1;
padding : 2;
flat_lighting : 1;
reversed_face : 1;
padding : 10;
ZBiasFlag zbias : 2;
position : 1;
padding : 1;
double_sided_face : 1;
disable_shadow : 1;
padding : 4;
} [[bitfield_order(std::core::BitfieldOrder::MostToLeastSignificant, 32)]];
struct Face {
FaceType type;
FacePoint points[4];
FaceFlags flags;
asciiz texture;
asciiz material;
} [[format("formatFace")]];
struct Edge {
u32 vertex_1;
u32 vertex_2;
} [[sealed,static,format("formatEdge")]];
struct Property {
char name[64] [[transform("std::string::to_string")]];
char value[64] [[transform("std::string::to_string")]];
} [[static,sealed,format("formatProperty")]];
struct Tagg {
bool active;
asciiz name;
u32 length;
if (name == "#EndOfFile#") {
break;
}
match (name) {
("#SharpEdges#"): Edge edges[length/8];
("#Property#"): Property property;
("#Mass#"): float masses[parent.count_verticies];
("#UVSet#"): {
u32 channel;
UV coordinates[(length - 4) / 8];
}
(_): if (std::string::starts_with(name, "#") && std::string::ends_with(name, "#")) {
u8 unknown_data[length];
} else {
u8 vertex_weights[parent.count_verticies];
u8 face_weights[parent.count_faces];
}
}
} [[format("formatTagg")]];
struct P3dmLod {
char signature[4];
u32 version_major;
u32 version_minor;
u32 count_verticies;
u32 count_normals;
u32 count_faces;
u32; // Unknown data (might be unused model flags)
Vertex verticies[count_verticies];
Vector normals[count_normals];
Face faces[count_faces];
char tagg_signature[4];
Tagg taggs[while(true)];
float resolution;
} [[format("formatP3dmLod")]];
struct P3D {
char signature[4];
u32 version;
u32 count_lods;
if (!process_all_lods) {
P3dmLod lod_0;
} else {
P3dmLod lods[count_lods];
}
};
fn formatVector(ref Vector pos) {
return std::format("[{0:.3f}, {1:.3f}, {1:.3f}]", pos.x, pos.y, pos.z);
};
fn formatUV(ref UV pos) {
return std::format("[{0:.3f}, {1:.3f}]", pos.u, pos.v);
};
fn formatVertex(ref Vertex vert) {
return formatVector(vert.position);
};
fn formatFace(ref Face face) {
return face.type == FaceType::TRIANGLE ? "triangle" : "quad";
};
fn formatEdge(ref Edge edge) {
return std::format("{0:d} <-> {1:d}", edge.vertex_1, edge.vertex_2);
};
fn formatProperty(ref Property prop) {
return std::format("\"{0:s}\" = \"{1:s}\"", prop.name, prop.value);
};
fn formatTagg(ref Tagg tagg) {
if (std::core::has_member(tagg, "vertex_weights")) {
return std::format("\"{0:s}\" selection", tagg.name);
} else {
return std::format("\"{0:s}\"", tagg.name);
}
};
fn formatP3dmLod(ref P3dmLod lod) {
return std::format("Resolution: {0}", lod.resolution);
};
P3D file @ 0x0000;

141
patterns/a3/a3_paa.hexpat Normal file
View File

@@ -0,0 +1,141 @@
#pragma author MrClock
#pragma description Arma 3 PAA image format
#pragma endian little
#pragma MIME image/x.a3-paa
fn get_data_description() {
return "PAA texture files are the proprietary image format used for textures in Arma 3.\nSimilar to most other formats used in game engines, the PAA stores not only a single resolution, but a series of precomputed mipmaps.\nPAA supports multiple pixel encoding formats, such as DXT1, DXT5, RGBA5551, grayscale, and others. Mipmap data in DXT encoded files is optionally compressed with the LZO1X algorithm. All other types are unconditionally LZSS compressed.";
};
import type.color;
import std.mem;
import std.sys;
struct Color<auto alpha> {
u8 b;
u8 g;
u8 r;
if (alpha) u8 a;
} [[sealed, format("type::impl::format_color"), color(std::format("{0:02X}{1:02X}{2:02X}", r, g, b))]];
using BGR8 = Color<false>;
using BGRA8 = Color<true>;
enum PixelFormat: u16 {
DXT1 = 0xFF01,
DXT2 = 0xFF02,
DXT3 = 0xFF03,
DXT4 = 0xFF04,
DXT5 = 0xFF05,
RGBA4 = 0x4444,
RGBA5 = 0x1555,
RGBA8 = 0x8888,
GRAY = 0x8080
};
enum AlphaMode: u32 {
NONE = 0,
INTERPOLATED = 1,
BINARY = 2
};
enum Swizzle: u8 {
ALPHA = 0,
RED = 1,
GREEN = 2,
BLUE = 3,
INVERTED_ALPHA = 4,
INVERTED_RED = 5,
INVERTED_GREEN = 6,
INVERTED_BLUE = 7,
BLANK_WHITE = 8,
BLANK_BLACK = 9
};
enum Compression: u8 {
NONE = 0,
LZO1X = 1,
LZSS = 2
};
struct Tagg {
char signature[8];
u32 length;
match (signature) {
("GGATCGVA"): BGRA8 color;
("GGATCXAM"): BGRA8 color;
("GGATGALF"): AlphaMode alpha;
("GGATSFFO"): u32 offsets[16];
("GGATZIWS"): Swizzle copies[4];
(_): u8 data[length];
}
} [[format("format_tagg_name")]];
struct Palette {
u16 length;
BGR8 colors[length];
};
struct Mipmap {
u16 width_and_lzo [[format("format_width_lzo")]];
u16 height;
u16 width = width_and_lzo;
Compression compression = Compression::NONE;
if ((u32(parent.format) & 0xFF00) == 0xFF00) {
width = width_and_lzo & 0x7FFF;
compression = width_and_lzo & 0x8000 ? Compression::LZO1X : Compression::NONE;
} else {
compression = Compression::LZSS;
}
if (width == 0 && height == 0) {
break;
}
u24 size;
match (compression) {
(Compression::NONE): u8 encoded_data[size];
(Compression::LZO1X): u8 lzo_compressed_data[size];
(Compression::LZSS): u8 lzss_compressed_data[size];
}
} [[format("format_resolution")]];
struct PAA {
PixelFormat format;
Tagg taggs[while(std::mem::read_string($, 4) == "GGAT")];
Palette palette;
Mipmap mipmaps[while(true)];
u16 EOF;
std::assert_warn(EOF == 0, "Invalid EOF sentinel");
};
fn format_resolution(ref auto mip) {
return std::format("{0:d} x {1:d}", mip.width, mip.height);
};
fn format_width_lzo(u16 value) {
u16 width = value & 0x7FFF;
if (value & 0x8000) {
return std::format("{0:d} (+LZO flag)", width);
} else {
return std::format("{0:d}", width);
}
};
fn format_tagg_name(Tagg data) {
match (data.signature) {
("GGATCGVA"): return "Average color";
("GGATCXAM"): return "Max color";
("GGATGALF"): return "Alpha flag";
("GGATZIWS"): return "Swizzle";
("GGATSFFO"): return "Mipmap offsets";
(_): return "Unknown";
}
};
PAA file @ 0x0000;

165
patterns/a3/a3_rap.hexpat Normal file
View File

@@ -0,0 +1,165 @@
#pragma author MrClock
#pragma description Arma 3 binary configuration format
#pragma endian little
#pragma MIME application/x.a3-rap
fn get_data_description() {
return "The RAP format is the binarized/\"rapified\" version of configuration files for Arma 3. Plain text configuration, material definition, scenario description and other files using the configuration syntax are rapified during the PBO packing process. The game can work with the plain text versions (they are actually rapified during boot), but properly converting them into the binary format ahead of time makes the booting easier.";
};
import std.mem;
import std.string;
import std.core;
import std.io;
using asciiz = std::string::NullString [[format("formatAsciiz")]];
/*
Item counts are stored in 7-bit encoded integers. In each byte the top bit signals
if the next byte belongs to the number as well.
*/
struct CompressedUint {
u8 extras[while(std::mem::read_unsigned($, 1) & 0x80)];
u8 last;
} [[sealed,transform("transformCompressedUint"),format("formatCompressedUint")]];
enum MemberType: u8 {
CLASS = 0,
LITERAL = 1,
ARRAY = 2,
EXTERNAL = 3,
DELETE = 4,
ARRAY_EXTENSION = 5
};
enum ValueType: u8 {
STRING = 0,
FLOAT = 1,
INTEGER = 2,
ARRAY = 3,
VARIABLE = 4
};
using Array;
struct ArrayItem {
ValueType type;
match (type) {
(ValueType::STRING): asciiz value;
(ValueType::FLOAT): float value;
(ValueType::INTEGER): s32 value;
(ValueType::ARRAY): Array value;
(ValueType::VARIABLE): asciiz value;
}
} [[format("formatArrayItem")]];
struct Array {
CompressedUint count_items;
ArrayItem items[count_items];
} [[format("formatArray")]];
using ClassBody;
struct Member {
MemberType type;
match(type) {
(MemberType::CLASS): {
asciiz name;
ClassBody *body : u32;
}
(MemberType::LITERAL): {
ValueType subtype;
asciiz name;
match (subtype) {
(ValueType::STRING): asciiz value;
(ValueType::FLOAT): float value;
(ValueType::INTEGER): s32 value;
(_): std::error(std::format("Unexpected subtype for literal: {}", subtype));
}
}
(MemberType::ARRAY | MemberType::ARRAY_EXTENSION): {
asciiz name;
Array value;
}
(MemberType::EXTERNAL): {
asciiz name;
}
(MemberType::DELETE): {
asciiz name;
}
}
} [[format("formatMember")]];
using Enums;
struct ClassBody {
asciiz parent_name;
CompressedUint count_members;
Member members[count_members];
u8 *pointer : u32 [[comment("In the root body this points to the enum list.\nIn all others it points to the next body on the same level.")]];
};
struct EnumItem {
asciiz name;
s32 value;
} [[format("formatEnumItem")]];
struct Enums {
u32 count_items;
EnumItem items[count_items];
};
struct RAP {
char signature[4];
u32;
u32;
Enums *enums : u32;
ClassBody root;
};
fn formatAsciiz(ref asciiz value) {
return std::format("\"{0:s}\"", value);
};
fn transformCompressedUint(ref CompressedUint value) {
u64 result = 0;
for (u8 i = 0, i < sizeof(value.extras), i += 1) {
result += (value.extras[i] & 0x7F) << (7*i);
}
result += value.last << (sizeof(value.extras) * 7);
return result;
};
fn formatCompressedUint(ref CompressedUint value) {
return value;
};
fn formatArrayItem(ref ArrayItem value) {
return value.value;
};
fn formatArray(ref Array value) {
return "{...}";
};
fn formatMember(ref Member item) {
match (item.type) {
(MemberType::CLASS): return std::format("class {0:s} {{...}};", item.name);
(MemberType::LITERAL): return std::format("{0:s} = {1};", item.name, item.value);
(MemberType::ARRAY): return std::format("{0:s}[] = {{...}};", item.name);
(MemberType::EXTERNAL): return std::format("class {0:s};", item.name);
(MemberType::DELETE): return std::format("del {0:s};", item.name);
}
};
fn formatEnumItem(ref EnumItem value) {
return std::format("{0:s} = {1}", item.name, item.value);
};
RAP file @ 0x0000;

93
patterns/a3/a3_rtm.hexpat Normal file
View File

@@ -0,0 +1,93 @@
#pragma author MrClock
#pragma description Arma 3 RTM animation format (plain)
#pragma endian little
#pragma MIME application/x.a3-rtm
fn get_data_description() {
return "Plain RTM animation files are used in animation authoring for Arma 3.\nThey can be created and edited in Object Builder.\nBone transformations are stored as absolute transformation matrices.\nPlain RTMs must be converted to their \"binarized\" versions by an appropriate PBO packing tool for use in game.";
};
import std.mem;
import std.sys;
import std.string;
import std.io;
using lascii = std::string::SizedString<u8> [[format("formatLascii")]];
struct Property {
float phase;
lascii name;
lascii value;
} [[format("formatProperty")]];
struct Bone {
char name[32];
} [[sealed,static,transform("transformBone"),format("formatBone")]];
struct Transform {
Bone bone;
float matrix[12] [[comment("4x4 transformation matrix (with last row omitted)")]];
} [[static,format("formatTransform")]];
struct Frame {
float phase;
Transform transforms[parent.count_bones];
} [[static,format("formatFrame")]];
struct Vector {
float x [[comment("+Left/-Right")]];
float y [[comment("+Up/-Down (UNUSED)")]];
float z [[comment("+Forward/-Backward")]];
} [[static,format("formatVector")]];
struct RTM {
if (std::mem::read_string($, 8) == "RTM_MDAT") {
char properties_signature[8];
padding[4];
u32 count_properties;
Property properties[count_properties];
}
std::assert(std::mem::read_string($, 8) == "RTM_0101", "Missing animation data");
char animation_signature[8];
Vector motion;
u32 count_frames;
u32 count_bones;
Bone bones[count_bones];
Frame frames[count_frames];
std::assert_warn(std::mem::eof(), "Data ended before EOF");
};
fn formatLascii(ref lascii value) {
return std::format("\"{:s}\"", value);
};
fn formatProperty(ref Property prop) {
return std::format("\"{0:s}\" = \"{1:s}\" @ {2:.4f}", prop.name, prop.value, prop.phase);
};
fn transformBone(ref Bone value) {
return std::string::to_string(value.name);
};
fn formatBone(ref Bone value) {
return std::format("\"{0:s}\"", value);
};
fn formatTransform(ref Transform transform) {
return std::format("\"{0:s}\" transform", transform.bone);
};
fn formatFrame(ref Frame frame) {
return std::format("frame @ {0:.4f}", frame.phase);
};
fn formatVector(ref Vector vec) {
return std::format("[{0:.2f}, {1:.2f}, {2:.2f}]", vec.x, vec.y, vec.z);
};
RTM file @ 0x0000;

View File

@@ -0,0 +1,131 @@
#pragma author MrClock
#pragma description Arma 3 texture index file format
#pragma endian little
#pragma MIME application/x.a3-texheaders
fn get_data_description() {
return "TexHeaders.bin files are texture index files used in Arma 3 PBO archives.\nThe files are generated during the PBO packing process, and contain basic information about all the PAA texture files in the PBO.\nThe index for each texture includes the file paths relative to the PBO root, pixel format, suffix, number of mipmaps and their resolutions among other things.";
};
import std.string;
import std.math;
import type.color;
using asciiz = std::string::NullString;
struct BGRA8 {
u8 b;
u8 g;
u8 r;
u8 a;
} [[
static,
sealed,
format("type::impl::format_color"),
color(std::format("{0:02X}{1:02X}{2:02X}", r, g, b))
]];
struct RGBAfloat {
float r [[transform("float2u8")]];
float g [[transform("float2u8")]];
float b [[transform("float2u8")]];
float a [[transform("float2u8")]];
} [[
static,
sealed,
format("type::impl::format_color"),
color(std::format("{0:02X}{1:02X}{2:02X}", r, g, b))
]];
enum PixelFormat: u8 {
INDEXED = 0,
GRAY = 1,
RGB565 = 2,
RGBA5551 = 3,
RGBA4444 = 4,
RGBA8888 = 5,
DXT1 = 6,
DXT2 = 7,
DXT3 = 8,
DXT4 = 9,
DXT5 = 10
};
enum Suffix: u32 {
DIFFUSE = 0,
DIFFUSE_LINEAR = 1,
DETAIL = 2,
NORMAL = 3,
IRRADIANCE = 4,
RANDOM = 5,
TREECROWN = 6,
MACRO = 7,
SHADOW = 8,
SPECULAR = 9,
DITHERING = 10,
DETAIL_SPECULAR = 11,
MASK = 12,
THERMAL = 13
};
struct Mipmap {
u16 width;
u16 height;
padding[2];
PixelFormat format;
padding[1];
u32 offset [[comment("Byte offset in file")]];
} [[static,format("formatMipmap")]];
struct Texture {
u32 count_color_pallets [[comment("Always 1")]];
u32 pallet_pointer [[comment("Always 0")]];
RGBAfloat average_color_float;
BGRA8 average_color;
BGRA8 max_color;
u32 clamp_flags [[comment("Always 0")]];
u32 transparency [[comment("Always 0xffffffff")]];
bool maxcolor_defined [[comment("GGATCXAM was found in PAA")]];
bool alpha_interpolated;
bool alpha_binary;
bool non_opaque [[comment("Interpolated alpha and average alpha < 127")]];
u32 count_mipmaps;
/*
Technically the format is listed as u32 on the community wiki, but
for the sake of reusability in the Mipmap struct, it is broken up into
a u8 + 3 padding bytes here. (The enum values are in the u8 range
anyway.)
*/
PixelFormat format;
padding[3];
bool little_endian [[comment("Always true")]];
bool is_paa [[comment("File is PAA not PAC")]];
asciiz path [[comment("Path relative to texHeaders.bin file")]];
Suffix suffix;
u32 count_mipmaps_again;
Mipmap mipmaps[count_mipmaps];
u32 filesize;
}[[format("formatTexture")]];
struct TexHeaders {
char signature[4];
u32 version [[comment("Always 1")]];
u32 count_textures;
Texture textures[count_textures];
};
fn float2u8(float value) {
return u8(std::math::round(value * 255));
};
fn formatMipmap(ref Mipmap mip) {
return std::format("{0:d} x {1:d}", mip.width, mip.height);
};
fn formatTexture(ref Texture tex) {
return std::format("{0:d} x {1:d} @ {2:s}", tex.mipmaps[0].width, tex.mipmaps[0].height, tex.path);
};
TexHeaders file @ 0x0000;

View File

@@ -0,0 +1,81 @@
#pragma author East_Arctica
#pragma description Chromium Pak File
import std.core;
import std.io;
struct IndexEntry {
u16 id;
u32 offset;
};
struct AliasEntry {
u16 id;
u16 index;
};
struct ResourceView {
u32 i = std::core::array_index();
u16 id = parent.entries[i].id;
u32 start = parent.entries[i].offset;
u32 end = parent.entries[i + 1].offset;
u32 length = (end >= start) ? (end - start) : 0;
u8 data[length] @ start;
// pak_util.py implies that this may also be gzip with a header of 0x1f 0x8b
bool is_brotli = (length > 8 && data[0] == 0x1e && data[1] == 0x9b);
if (is_brotli) {
u8 magic[2] @ start;
u48 decompressed_size @ start + 2;
// TODO: If brotli decompression is added to ImHex, add it here.
}
};
struct AliasResourceView {
u32 i = std::core::array_index();
u16 id = parent.aliases[i].id;
u16 idx = parent.aliases[i].index;
bool valid = (idx < parent.resource_count);
u32 start = valid ? parent.entries[idx].offset : 0;
u32 end = valid ? parent.entries[idx + 1].offset : 0;
u32 length = (valid && end >= start) ? (end - start) : 0;
u8 data[length] @ start;
};
enum PakEncoding : u8 {
binary = 0,
utf8 = 1,
utf16 = 2
};
struct Pak {
u32 version;
if (version == 4) {
u32 resource_count;
PakEncoding encoding;
IndexEntry entries[resource_count + 1];
ResourceView resources[resource_count];
} else if (version == 5) {
PakEncoding encoding;
padding[3];
u16 resource_count;
u16 alias_count;
IndexEntry entries[resource_count + 1];
AliasEntry aliases[alias_count];
ResourceView resources[resource_count];
AliasResourceView alias_resources[alias_count];
} else {
std::error("Unsupported pak version");
}
};
// Some pack files are stored gzipped and need to be gunzipped first
Pak pak @ 0x00;

View File

@@ -0,0 +1,46 @@
#pragma author gunjambi
#pragma description "New" ASCII CPIO
#pragma magic [ 30 37 30 37 30 31 ] @ 0x00
import std.time;
import std.string;
namespace new_ascii {
fn format_hex_time(str value) {
return std::time::format(std::time::to_utc(std::string::parse_int(value, 16)));
};
fn parse_hex(str field) {
return std::string::parse_int(field, 16);
};
struct Cpio {
char c_magic[6];
if (c_magic != "070701") {
std::error("Invalid CPIO Magic!");
}
char c_ino[8] [[format("new_ascii::parse_hex"), transform("new_ascii::parse_hex")]];
char c_mode[8];
char c_uid[8] [[format("new_ascii::parse_hex"), transform("new_ascii::parse_hex")]];
char c_gid[8] [[format("new_ascii::parse_hex"), transform("new_ascii::parse_hex")]];
char c_nlink[8] [[format("new_ascii::parse_hex"), transform("new_ascii::parse_hex")]];
char c_mtime[8] [[format("new_ascii::format_hex_time")]];
char c_filesize[8] [[format("new_ascii::parse_hex"), transform("new_ascii::parse_hex")]];
char c_devmajor[8] [[format("new_ascii::parse_hex"), transform("new_ascii::parse_hex")]];
char c_devminor[8] [[format("new_ascii::parse_hex"), transform("new_ascii::parse_hex")]];
char c_rdevmajor[8] [[format("new_ascii::parse_hex"), transform("new_ascii::parse_hex")]];
char c_rdevminor[8] [[format("new_ascii::parse_hex"), transform("new_ascii::parse_hex")]];
char c_namesize[8] [[format("new_ascii::parse_hex"), transform("new_ascii::parse_hex")]];
char c_check[8] [[format("new_ascii::parse_hex"), transform("new_ascii::parse_hex")]];
char filename[c_namesize - 1];
padding[1];
if (filename == "TRAILER!!!")
break;
padding[(4 - ((6 + c_namesize) % 4)) % 4];
u8 data[c_filesize];
padding[(4 - (c_filesize % 4)) % 4];
};
}
new_ascii::Cpio cpio[while(true)] @ 0x00;

View File

@@ -0,0 +1,41 @@
#pragma author gunjambi
#pragma description Portable ASCII CPIO
#pragma magic [ 30 37 30 37 30 37 ] @ 0x00
import std.time;
import std.string;
namespace portable_ascii {
fn format_octal_time(str value) {
return std::time::format(std::time::to_utc(std::string::parse_int(value, 8)));
};
fn parse_octal_string(str field) {
return std::string::parse_int(field, 8);
};
struct Cpio {
char c_magic[6];
if (c_magic != "070707") {
std::error("Invalid CPIO Magic!");
}
char c_dev[6] [[format("portable_ascii::parse_octal_string"), transform("portable_ascii::parse_octal_string")]];
char c_ino[6] [[format("portable_ascii::parse_octal_string"), transform("portable_ascii::parse_octal_string")]];
char c_mode[6];
char c_uid[6] [[format("portable_ascii::parse_octal_string"), transform("portable_ascii::parse_octal_string")]];
char c_gid[6] [[format("portable_ascii::parse_octal_string"), transform("portable_ascii::parse_octal_string")]];
char c_nlink[6] [[format("portable_ascii::parse_octal_string"), transform("portable_ascii::parse_octal_string")]];
char c_rdev[6] [[format("portable_ascii::parse_octal_string"), transform("portable_ascii::parse_octal_string")]];
char c_mtime[11] [[format("portable_ascii::format_octal_time")]];
char c_namesize[6] [[format("portable_ascii::parse_octal_string"), transform("portable_ascii::parse_octal_string")]];
char c_filesize[11] [[format("portable_ascii::parse_octal_string"), transform("portable_ascii::parse_octal_string")]];
char filename[c_namesize - 1];
padding[1];
if (filename == "TRAILER!!!")
break;
u8 data[c_filesize];
};
}
portable_ascii::Cpio cpio[while(true)] @ 0x00;

View File

@@ -473,9 +473,37 @@ bitfield SYMINFO_FLG {
padding : 10;
};
enum STT : u32 {
STT_NOTYPE = 0, /* Symbol type is unspecified */
STT_OBJECT = 1, /* Symbol is a data object */
STT_FUNC = 2, /* Symbol is a code object */
STT_SECTION = 3, /* Symbol associated with a section */
STT_FILE = 4, /* Symbol's name is file name */
STT_COMMON = 5, /* Symbol is a common data object */
STT_TLS = 6, /* Symbol is thread-local data object */
STT_NUM = 7, /* Number of defined types. */
STT_LOOS = 10, /* Start of OS-specific */
STT_GNU_IFUNC = 10, /* Symbol is indirect code object */
STT_HIOS = 12, /* End of OS-specific */
STT_LOPROC = 13, /* Start of processor-specific */
STT_HIPROC = 15, /* End of processor-specific */
};
enum STB : u32 {
STB_LOCAL = 0, /* Local symbol */
STB_GLOBAL = 1, /* Global symbol */
STB_WEAK = 2, /* Weak symbol */
STB_NUM = 3, /* Number of defined types. */
STB_LOOS = 10, /* Start of OS-specific */
STB_GNU_UNIQUE = 10, /* Unique symbol. */
STB_HIOS = 12, /* End of OS-specific */
STB_LOPROC = 13, /* Start of processor-specific */
STB_HIPROC = 15, /* End of processor-specific */
};
bitfield ST {
ST_BIND : 4;
ST_TYPE : 4;
STB ST_BIND : 4;
STT ST_TYPE : 4;
} [[bitfield_order(BitfieldOrder::MostToLeastSignificant, 8)]];
bitfield SHF {

61
patterns/gbx.hexpat Normal file
View File

@@ -0,0 +1,61 @@
#pragma author gmestanley
#pragma description GameBoy ROM file GBX footer
#pragma source hhug.me/gbx/1.0
import gb;
bitfield SachenMMC2SolderPad {
padding : 5;
opt1 : 1;
openBus512KiBOuterBanks : 1;
openBus1MiBOuterBanks : 1;
};
bitfield VastFameRunningValueType {
TaiwanReleases : 1;
MainlandChinaReleases : 1;
};
enum VastFamePCBType : u8 {
DSHGGB81,
BCR1616T3P
};
fn specialMapper(str value) { return value == "SAM2" || value == "VF01" || value == "GB81"; };
struct MapperVariables {
match(parent.mapper) {
("SAM2"): SachenMMC2SolderPad solderPadConfig;
("VF01"): VastFameRunningValueType runningValue;
("GB81"): VastFamePCBType pcb; }
padding[3*specialMapper(parent.mapper)];
u32 mapperVariables[8-specialMapper(parent.mapper)];
};
struct CartridgeInformation {
char mapper[4];
bool battery;
bool rumble;
bool timer;
padding[1];
u32 romSize;
u32 ramSize;
if (specialMapper(mapper))
MapperVariables mapperVariables;
else
MapperVariables mapperVariables [[inline]];
};
struct GBXMetadata {
u32 footerSize;
u32 majorVersion;
u32 minorVersion;
char signature[4];
};
struct GBXFooter {
CartridgeInformation cartInfo;
GBXMetadata metadata;
};
be GBXFooter gbxFooter @ std::mem::size() - 0x40;

137
patterns/gen.hexpat Normal file
View File

@@ -0,0 +1,137 @@
#pragma author gmestanley
#pragma description Sega Genesis/MegaDrive header
#pragma source plutiedev.com/rom-header wiki.neogeodev.org/index.php?title=68k_vector_table
#pragma endian big
import std.string;
struct M68000Vectors {
u32 stackPointerReset;
u32 programCounterReset [[comment("Entry Point")]];
u32 busError;
u32 addressError;
u32 illegalInstruction;
u32 divisionByZero;
u32 chkInstruction;
u32 trapVInstruction;
u32 privilegeViolation;
u32 trace;
u32 lineAInstruction;
u32 lineFInstruction;
padding[12];
u32 uninitializedInterruptVector;
padding[32];
u32 spuriousInterrupt;
u32 interruptAutovectors[7];
u32 traps[16];
};
M68000Vectors vectors @ 0x00;
struct Info {
char softwareType[2];
char space;
char serialNumber[8];
char dash;
char revision[2];
};
enum DeviceType : char {
NotFilled = ' ',
Button3Controller = 'J',
Button6Controller = '6',
MasterSystemController = '0',
AnalogJoystick = 'A',
Multitap = '4',
Lightgun = 'G',
Activator = 'L',
Mouse = 'M',
Trackball = 'B',
Mouse = 'T',
Trackball = 'V',
Keyboard = 'K',
RS232 = 'R',
Printer = 'P',
CDROM = 'C',
FloppyDrive = 'F',
Download = 'D'
};
bitfield RAMType {
addresses : 1;
padding : 3;
bits : 2;
saves : 1;
sig : 1;
};
enum MemoryType : char {
RAM = ' ',
EEPROM = '@'
};
struct ExtraMemory {
char signature[2];
RAMType ramType;
MemoryType memoryType;
u32 startAddress;
u32 endAddress;
};
fn renderMicrophoneType(str value) {
match(value) {
("00"): value = "NoMicJapanOnly";
("10"): value = "MicJapanOnly";
("20"): value = "NoMicOverseasOnly";
("30"): value = "MicOverseasOnly";
("40"): value = "NoMic";
("50"): value = "Mic";
("60"): value = "NoMicJapan";
("70"): value = "NoMicOverseas";
}
};
struct ModemSupport {
char signature[2];
char publisher[4];
char gameNumber[2];
char comma;
char version;
char microphone[2] [[format("renderMicrophoneType")]];
};
enum RegionType : char {
None = ' ',
Japan = 'J',
Americas = 'U',
Europe = 'E'
};
fn formatTerminatedString(str string) {
u8 index;
while (index < std::string::length(string)) {
if (std::mem::read_string($+index, 2) == " ")
break;
index += 1;
}
return "\"" + std::string::substr(string, 0, index) + "\"";
};
struct Header {
char systemType[16] [[format("formatTerminatedString")]];
char copyright[16] [[format("formatTerminatedString")]];
char domesticTitle[48] [[format("formatTerminatedString")]];
char overseasTitle[48] [[format("formatTerminatedString")]];
Info info;
u16 checksum;
DeviceType deviceType[16];
u32 romAddressRange[2];
u32 ramAddressRange[2];
if ($[$] == 'R') ExtraMemory extraMemory; else padding[12];
if ($[$] == 'M') ModemSupport modemSupport; else padding[12];
padding[40];
RegionType regions[3];
};
Header header @ 0x100;

109
patterns/lua50.hexpat Normal file
View File

@@ -0,0 +1,109 @@
#pragma description Lua 5.0 bytecode
#pragma magic [ 1B 4C 75 61 50 ] @ 0x00
import std.io;
namespace impl {
fn format_LuaString(auto string) {
if (string.size == 0) {
return "None";
}
return std::format("\"{}\"", string.data);
};
fn format_Constant(auto constant) {
return constant.data;
};
fn format_Version(auto ver) {
return std::format("Ver. {}.{}", ver.major, ver.minor);
};
}
using LuaFunction;
bitfield Version {
minor : 4;
major : 4;
} [[format("impl::format_Version")]];
struct LuaBinaryHeader {
char magic[4];
Version version_number;
u8 endianness;
u8 size_of_int;
u8 size_of_size_t;
u8 size_of_Instruction;
u8 size_of_op;
u8 size_of_a;
u8 size_of_b;
u8 size_of_c;
u8 size_of_lua_Number;
double test_number;
};
LuaBinaryHeader header @ 0;
struct LuaString {
if (header.size_of_size_t == 4) {
u32 size;
} else {
u64 size;
}
if (size > 0) {
char data[size];
}
}[[format("impl::format_LuaString")]];
struct LuaConstant {
u8 type;
if (type == 0) { // LUA_TNIL
// NULL
} else if (type == 1) { // LUA_TBOOLEAN
u8 data;
} else if (type == 3) { // LUA_TNUMBER
double data;
} else if (type == 4) { // LUA_TSTRING
LuaString data;
}
}[[format("impl::format_Constant")]];
struct Vector<T> {
u32 size;
if (size > 0) {
T values[size];
}
};
struct LocalVar {
LuaString varname;
u32 startpc;
u32 endpc;
};
struct LuaDebugInfo {
Vector<u32> lineInfo;
Vector<LocalVar> localVar;
Vector<LuaString> upvalues;
};
struct LuaConstants{
Vector<LuaConstant> constants;
u32 sizep; // size of proto
if (sizep > 0) {
LuaFunction protos[sizep];
}
};
struct LuaFunction {
LuaString source;
u32 linedefined;
u8 nups; // number of upvalues
u8 numparams;
u8 is_vararg;
u8 maxstacksize;
LuaDebugInfo debugInfo;
LuaConstants luaConstants;
Vector<u32> code;
};
LuaFunction toplevelFunction @ 22;

4
patterns/n64.hexpat Normal file
View File

@@ -0,0 +1,4 @@
#pragma author gmestanley
#pragma description Nintendo 64 ROM header
char name[20] @ 0x20;

View File

@@ -4,404 +4,540 @@
import std.string;
enum MirroringType : u8 {
Vertical,
Horizontal
};
fn mirroring(u8 value) {
MirroringType enumValue = value;
return enumValue;
};
bitfield Flags {
mirroringIsHorizontal : 1;
ignoreMirroring : 1;
batterybackedPRGRAM : 1;
trainerOf512Bytes : 1;
lowerMapperNybble : 4;
mirroring : 1 [[format("mirroring")]];
ignoreMirroring : 1;
batterybackedPRGRAM : 1;
trainerOf512Bytes : 1;
lowerMapperNybble : 4;
};
enum ConsoleType : u8 {
Regular,
VsSystem,
PlayChoice10,
ExtendedConsoleType
Regular,
VsSystem,
PlayChoice10,
ExtendedConsoleType
};
fn consoleType(u8 bits) {
ConsoleType type = bits;
return type;
ConsoleType type = bits;
return type;
};
fn nes2Format(u8 bits) {
return std::string::to_string(bits == 2) + " (" + std::string::to_string(bits) + ")";
return std::string::to_string(bits == 2) + " (" + std::string::to_string(bits) + ")";
};
bitfield iNESFlags7 {
consoleType : 2 [[format("consoleType")]];
nes2Format : 2 [[format("nes2Format"), name("nes2.0Format")]];
higherMapperNybble : 4;
consoleType : 2 [[format("consoleType")]];
nes2Format : 2 [[format("nes2Format"), name("nes2.0Format")]];
higherMapperNybble : 4;
};
enum OldTVSystemByte : u8 {
NTSC,
PAL
};
fn formatOldTVSystemByte(u8 value) {
OldTVSystemByte enumValue = value;
return value;
};
bitfield iNESFlags9 {
tvSystem : 1 [[comment("0 = NTSC, 1 = PAL")]];
padding : 7;
tvSystem : 1 [[format("formatOldTVSystemByte")]];
padding : 7;
};
fn formatDualTVSystem(u8 bits) {
match (bits) {
(0): return "NTSC";
(2): return "PAL";
(1 || 3): return "Dual Compatible";
}
match (bits) {
(0): return "NTSC";
(2): return "PAL";
(1 || 3): return "Dual Compatible";
}
};
bitfield iNESFlags10 {
dualTVSystem : 2 [[format("formatDualTVSystem")]];
padding : 2;
prgRAM : 1;
busConflicts : 1;
dualTVSystem : 2 [[format("formatDualTVSystem")]];
padding : 2;
prgRAM : 1;
busConflicts : 1;
};
bitfield MapperExtra {
highestMapperNybble : 4;
submapper : 4;
highestMapperNybble : 4;
submapper : 4;
};
bitfield ROMSize {
extraPRGROMSize : 4;
extraCHRROMSize : 4;
extraPRGROMSize : 4;
extraCHRROMSize : 4;
};
bitfield PRGRAMSize {
prgRAMShiftCount : 4;
eepromShiftCount : 4 [[comment("EEPROM = Non-volatile PRG RAM")]];
prgRAMShiftCount : 4;
eepromShiftCount : 4 [[comment("EEPROM = Non-volatile PRG RAM, or NVPRGRAM")]];
};
bitfield CHRRAMSize {
chrRAMSizeShiftCount : 4;
chrNVRAMSizeShiftCount : 4;
chrRAMSizeShiftCount : 4;
chrNVRAMSizeShiftCount : 4;
};
enum TimingList : u8 {
NTSC,
PAL,
MultiRegion,
Dendy
NTSC,
PAL,
MultiRegion,
Dendy
};
fn Timing(u8 value) {
TimingList type = value;
return type;
TimingList enumValue = value;
return enumValue;
};
bitfield Timing {
processorTiming : 2 [[format("Timing")]];
padding : 6;
bitfield Timing {
processorTiming : 2 [[format("Timing")]];
padding : 6;
};
bitfield VsSystemType {
vsPPUType : 4;
vsHardwareType: 4;
vsPPUType : 4;
vsHardwareType: 4;
};
enum ExtendedConsoleType : ConsoleType {
DecimalModeFamiclone = 3,
PlugThrough,
VT01,
VT02,
VT03,
VT09,
VT32,
VT3xx,
UM6578,
FamicomNetworkSystem
DecimalModeFamiclone = 3,
PlugThrough,
VT01,
VT02,
VT03,
VT09,
VT32,
VT36x,
UM6578,
FamicomNetworkSystem
};
fn formatExtendedConsoleType(u8 nybble) {
ExtendedConsoleType type = nybble;
return type;
ExtendedConsoleType type = nybble;
return type;
};
bitfield ExtendedConsoleTypeByte {
type : 4 [[format("formatExtendedConsoleType")]];
padding : 4;
type : 4 [[format("formatExtendedConsoleType")]];
padding : 4;
};
bitfield MiscellaneousROMs {
numberOfMiscellaneousROMs : 2;
padding : 6;
bitfield MiscellaneousROMsHeaderByte {
numberOfMiscellaneousROMs : 2;
padding : 6;
};
enum DefaultExpansionDeviceType : u8 {
Unspecified,
StandardControllers,
FourScoreorSatellite,
FamicomFourPlayersAdapter,
VsSystem1P4016h,
VsSystem1P4017h,
MAMEPinballJapan,
VsZapper,
Zapper,
TwoZappers,
BandaiHyperShotLightgun,
PowerPadSideA,
PowerPadSideB,
FamilyTrainerSideA,
FamilyTrainerSideB,
Multicart = 0x2A
};
fn formatDefaultExpansionDevice(u8 value) {
DefaultExpansionDeviceType enumValue = value;
return enumValue;
};
bitfield DefaultExpansionDevice {
defaultExpansionDevice : 6;
defaultExpansionDevice : 6 [[format("formatDefaultExpansionDevice")]];
};
struct NES2Attributes {
MapperExtra mapperExtra;
ROMSize romSize;
PRGRAMSize prgRAMSize;
CHRRAMSize chrRAMSize;
Timing timing;
if (parent.inesFlags7.consoleType == ConsoleType::VsSystem) {
VsSystemType vsSystemType;
}
else if (parent.inesFlags7.consoleType == ConsoleType::ExtendedConsoleType) {
ExtendedConsoleTypeByte ExtendedConsoleTypeByte;
}
else {
padding[1];
}
MiscellaneousROMs miscellaneousROMs;
DefaultExpansionDevice defaultExpansionDevice;
MapperExtra mapperExtra;
ROMSize romSize;
PRGRAMSize prgRAMSize;
CHRRAMSize chrRAMSize;
Timing timing;
if (parent.inesFlags7.consoleType == ConsoleType::VsSystem) {
VsSystemType vsSystemType;
}
else if (parent.inesFlags7.consoleType == ConsoleType::ExtendedConsoleType) {
ExtendedConsoleTypeByte ExtendedConsoleTypeByte;
}
else {
padding[1];
}
MiscellaneousROMsHeaderByte miscellaneousROMs;
DefaultExpansionDevice defaultExpansionDevice;
} [[inline]];
fn renderEOF(str string) {
return "\"NES<EOF>\"";
return "\"NES<EOF>\"";
};
u8 FILLED_NES2_FLAGS = 0b1100;
struct Header {
char identifier[4] [[format("renderEOF")]];
u8 prgROMSizeBy16KiBs;
u8 chrROMSizeBy8KiBs;
Flags flags;
if ($[0x07] & 12 != 4) {
iNESFlags7 inesFlags7;
if (inesFlags7.nes2Format)
NES2Attributes nes2Attributes;
else if ($[0x07] & 12 == 0 && !std::mem::read_unsigned($, 4)) {
u8 prgRAMSizeBy8KiBs;
iNESFlags9 inesFlags9;
iNESFlags10 inesFlags10;
}
}
char identifier[4] [[format("renderEOF")]];
u8 prgROMSizeBy16KiBs;
u8 chrROMSizeBy8KiBs;
Flags flags;
if ($[0x07] & FILLED_NES2_FLAGS != 0b100) {
iNESFlags7 inesFlags7;
if (inesFlags7.nes2Format)
NES2Attributes nes2Attributes;
else if ($[0x07] & FILLED_NES2_FLAGS == 0 && !std::mem::read_unsigned(0x0C, 4)) {
u8 prgRAMSizeBy8KiBs;
iNESFlags9 inesFlags9;
iNESFlags10 inesFlags10;
}
}
};
Header header @ 0x00;
u8 FILLED_HIGHER_NYBBLE = 0b1111;
u8 SET_NES2_FLAGS = 0b1000;
u16 mapperValue = (0x0100 * ($[0x08] & FILLED_HIGHER_NYBBLE)) * ($[7] & FILLED_NES2_FLAGS == SET_NES2_FLAGS)
+ (0x10 * ($[0x07] >> 4)) * ($[0x07] & FILLED_NES2_FLAGS != 0b100)
+ header.flags.lowerMapperNybble;
fn identifyMapper(u16 mapperValue, u8 submapperValue) {
str mapper;
str submapper;
str designation;
match (mapperValue) {
(0): mapper = "No mapper";
(1): mapper = "Nintendo MMC1B";
(2): mapper = "UxROM";
(3): mapper = "CNROM-32";
(4): mapper = "MMC3";
(5): mapper = "MMC5";
(6): mapper = "Front Fareast Magic Card 1/2M RAM Cartridge";
(7): mapper = "AxROM";
(8): mapper = "Front Fareast Magic Card 1/2M RAM Cartridge [Initial latch-based banking mode 4]";
(9): mapper = "MMC2";
(10): mapper = "MMC4";
(11): mapper = "Color Dreams";
(12): mapper = "[See submapper]";
(13): mapper = "CPROM";
(14): mapper = "SL-1632";
(15): mapper = "K-102xx";
(16): mapper = "Bandai FCG boards";
(17): mapper = "Front Fareast Super Magic Card RAM cartridge";
(18): mapper = "Jaleco SS88006";
(19): mapper = "Namco 129/163";
(20): mapper = "Famicom Disk System";
(21): mapper = "Konami VRC4a/VRC4c";
(22): mapper = "Konami VRC2a";
(23): mapper = "Konami VRC4e or VRC2b + VRC4f";
(24): mapper = "Konami VRC6a";
(25): mapper = "Konami VRC4d or VRC2c + VRC4b";
(26): mapper = "Konami VRC6b";
(27): mapper = "World Hero";
(28): mapper = "InfiniteNESLives' Action 53";
(29): mapper = "RET-CUFROM";
(30): mapper = "UNROM 512";
(31): mapper = "NSF";
(32): mapper = "G-101";
(33): mapper = "Taito TC0190";
(34): mapper = "[See submapper]";
(35): mapper = "J.Y. Company ASIC [8KiB WRAM]";
(36): mapper = "Micro Genius 01-22000-400";
(37): mapper = "SMB+Tetris+NWC";
(38): mapper = "Bit Corp. Crime Busters";
(39): mapper = "Study & Game 32-in-1 [DEPRECATED]";
(40): mapper = "NTDEC 27xx";
(41): mapper = "Caltron 6-in-1";
(42): mapper = "FDS -> NES Hacks";
(43): mapper = "TONY-I/YS-612";
(44): mapper = "Super Big 7-in-1";
(45): mapper = "GA23C"; //TC3294 according to NintendulatorNRS
(46): mapper = "Lite Star's Rumble Station";
(47): mapper = "Nintendo Super Spike V'Ball + NWC";
(48): mapper = "Taito TC0690";
(49): mapper = "Super HIK 4-in-1";
(50): mapper = "N-32 [Super Mario Bros. 2 (J)]";
(51): mapper = "[See submapper]";
(52): mapper = "Realtec 8213";
(53): mapper = "Supervision 16-in-1";
(54): mapper = "Novel Diamond 9999999-in-1 [DEPRECATED]";
(55): mapper = "QFJxxxx";
(56): mapper = "KS202";
(57): mapper = "GK";
(58): mapper = "WQ";
(59): mapper = "T3H53";
(60): mapper = "Reset-based NROM-128 4-in-1";
(61): mapper = "[See submapper]";
(62): mapper = "Super 700-in-1";
(63): mapper = "[See submapper]";
(64): mapper = "Tengen RAMBO-1";
(65): mapper = "Irem H3001";
(66): mapper = "xxROM";
(67): mapper = "Sunsoft-3";
(68): mapper = "Sunsoft-4";
(69): mapper = "Sunsoft FME-7/Sunsoft 5A/Sunsoft 5B";
(70): mapper = "Family Trainer Mat";
(71): mapper = "Camerica";
(72): mapper = "Jaleco JF-17 [16 KiB PRG ROM]";
(73): mapper = "Konami VRC3";
(74): mapper = "860908C";
(75): mapper = "Konami VRC1";
(76): mapper = "NAMCOT-3446";
(77): mapper = "Napoleon Senki";
(78): mapper = "74HC161/32";
(79): mapper = "Tengen NINA-003/NINA-006";
(80): mapper = "X1-005";
(81): mapper = "N715021";
(82): mapper = "X1-017";
(83): mapper = "Cony & Yoko chip";
(84): mapper = "PC-SMB2J [DEPRECATED]";
(85): mapper = "Konami VRC7";
(86): mapper = "Jaleco JF-13";
(87): mapper = "JF87";
(88): mapper = "Namco chip";
(89): mapper = "Sunsoft 2.5";
(90): mapper = "J.Y. Company ASIC [ROM nametables & extended mirroring]";
(91): mapper = "J.Y. Company clone boards";
(92): mapper = "Jaleco JF-17 [16 KiB PRG ROM]";
(124): mapper = "Super Game Mega Type III";
(126): mapper = "TEC9719";
(132): mapper = "TXC 05-00002-010";
(173): mapper = "C&E 05-00002-010";
(187): mapper = "Kǎ Shèng A98402";
(256): mapper = "V.R. Technology OneBus";
(269): mapper = "Nice Code Games Xplosion 121-in-1";
(355): mapper = "Jùjīng 3D-BLOCK";
(419): mapper = "Taikee TK-8007 MCU";
(422): mapper = "ING-022";
(423): mapper = "Lexibook Compact Cyber Arcade";
(424): mapper = "Lexibook Retro TV Game Console";
(425): mapper = "Cube Tech Handheld";
(426): mapper = "V.R. Technology OneBus [Serial ROM in GPIO]";
(534): mapper = "ING003C";
(594): mapper = "Rinco FSG2";
(595): mapper = "NES-4MROM-512";
}
match (mapperValue) {
(0): designation = "NROM";
(1): designation = "SxROM";
(4): {
if (header.prgROMSizeBy16KiBs >= 8 && header.chrROMSizeBy8KiBs >= 16) {
if (($[0x08] != 1 && ($[0x07] & FILLED_NES2_FLAGS == 0 && !std::mem::read_unsigned(0x0C, 4))) ||
($[0x0A]&FILLED_HIGHER_NYBBLE!=7 && ($[0x07] & FILLED_NES2_FLAGS == SET_NES2_FLAGS)))
designation = "TLROM [128~512 KiBs PRG ROM]";
else {
if (header.flags.batterybackedPRGRAM) designation = "TKROM [128~512 KiBs PRG ROM, 8 KiB PRG RAM]";
else designation = "TSROM [128~512 KiBs PRG ROM, 8 KiB PRG RAM, no battery]";
}
}
else if (header.prgROMSizeBy16KiBs == 4) designation = "TBROM [64 KiBs PRG ROM]";
else designation = "TxROM";
}
}
if (mapperValue == 3) {
match (submapperValue) {
(0): submapper = "Bus conflict";
(1): submapper = "No bus conflicts";
(2): submapper = "AND-type bus conflicts";
}
}
else if (mapperValue == 12) {
match (submapperValue) {
(0): submapper = "Supertone SL-5020B";
(1): submapper = "Front Fareast Magic Card 4M RAM Cartridge";
}
}
else if (mapperValue == 16) {
match (submapperValue) {
(0): submapper = "Both Bandai FCG-1/2 and Bandai LZ93D50";
(4): submapper = "Bandai FCG-1/2";
(5): submapper = "Bandai LZ93D50";
}
}
else if (mapperValue == 34) {
match (submapperValue) {
(0): submapper = "Tengen NINA-001/NINA-002";
(1): submapper = "BNROM";
}
}
else if (mapperValue == 40) {
match (submapperValue) {
(0): submapper = "NTDEC 2722";
(1): submapper = "NTDEC 2752";
}
}
else if (mapperValue == 51) {
if (submapperValue == 1) submapper = "11-in-1 Ball Games";
}
else if (mapperValue == 61) {
match (submapperValue) {
(0): submapper = "NTDEC 0324";
(1): submapper = "NTDEC BS-N032";
(_): submapper = "GS-2017";
}
}
else if (mapperValue == 63) {
match (submapperValue) {
(0): submapper = "NTDEC TH2xxx-x";
(1): submapper = "82-in-1";
}
}
else if (mapperValue == 256 || mapperValue == 419) {
match (submapperValue) {
(0): submapper = "Normal";
(1): submapper = "Waixing VT03";
(2): submapper = "Nice Code VT02";
(3): submapper = "Hummer Technology";
(4): submapper = "Nice Code VT03";
(5): submapper = "Waixing VT02";
(11): submapper = "Vibes";
(12): submapper = "Cheertone";
(13): submapper = "Cube Tech";
(14): submapper = "Unknown developer (Publisher: Karaoto)";
(15): submapper = "JungleTac Fuzhou";
}
submapper += " wiring";
}
std::print("Mapper: " + mapper + " (" + std::string::to_string(mapperValue) + ")");
if (submapper) std::print("Submapper: " + submapper + " (" + std::string::to_string(submapperValue) + ")");
if (designation) std::print("Designation: " + designation);
};
identifyMapper(mapperValue, $[0x08] >> 4);
u8 trainer[512*header.flags.trainerOf512Bytes] @ 0x10;
enum CHRType : u8 {
CHRROM,
CHRRAM
CHRROM,
CHRRAM
};
fn chrType(u8 value) {
CHRType enumValue = value;
return enumValue;
CHRType enumValue = value;
return enumValue;
};
fn chrSize(u8 value) {
u24 actualSize;
if (value == 4) actualSize = 262144;
else actualSize = 8192 * header.chrROMSizeBy8KiBs;
return std::string::to_string(value) + " (" + std::string::to_string(actualSize) + ")";
u24 actualSize;
if (value == 4) actualSize = 262144;
else actualSize = 8192 * header.chrROMSizeBy8KiBs;
return std::string::to_string(value) + " (" + std::string::to_string(actualSize) + ")";
};
bitfield MemorySize {
prgROMSizeBy16KiBs : 4;
chrType : 1 [[format("chrType")]];
chrSize : 3 [[format("chrSize")]];
prgROMSizeBy16KiBs : 4;
chrType : 1 [[format("chrType")]];
chrSize : 3 [[format("chrSize")]];
};
enum ArrangementList : u8 {
Horizontal,
Vertical
Horizontal,
Vertical
};
fn arrangement(u8 value) {
ArrangementList enumValue = value;
return enumValue;
ArrangementList enumValue = value;
return enumValue;
};
enum MapperList : u8 {
NROM,
CNROM,
UNROM,
GNROM,
MMC
NROM,
CNROM,
UNROM,
GNROM,
MMC
};
fn mapper(u8 value) {
MapperList enumValue = value;
return enumValue;
MapperList enumValue = value;
return enumValue;
};
bitfield CartridgeType {
nametableArrangement : 1 [[format("arrangement")]];
mapper : 7 [[format("mapper")]];
nametableArrangement : 1 [[format("arrangement")]];
mapper : 7 [[format("mapper")]];
};
enum EncodingType : u8 {
None,
ASCII,
JIS
None,
ASCII,
JIS
};
fn titleLength(u8 value) { return value+1; };
struct OfficialHeader {
char title[16] [[hex::spec_name("Title Registration Area")]];
u16 programChecksum;
u16 characterChecksum;
MemorySize memorySize [[hex::spec_name("Cartridge Memory Size")]];
CartridgeType cartridgeType;
EncodingType encodingType [[hex::spec_name("Registration Characters Type Distinction")]];
u8 titleLength [[hex::spec_name("Registration Characters Count"), transform("titleLength")]];
u8 licenseeID [[hex::spec_name("Maker Code")]];
u8 complementaryChecksum [[hex::spec_name("Checksum for characterChecksum~makerID")]];
struct Footer {
char title[16] [[hex::spec_name("Title Registration Area")]];
u16 programChecksum;
u16 characterChecksum;
MemorySize memorySize [[hex::spec_name("Cartridge Memory Size")]];
CartridgeType cartridgeType;
EncodingType encodingType [[hex::spec_name("Registration Characters Type Distinction")]];
u8 titleLength [[hex::spec_name("Registration Characters Count"), transform("titleLength")]];
u8 licenseeID [[hex::spec_name("Maker Code")]];
u8 complementaryChecksum [[hex::spec_name("Checksum for Character Checksum~Maker Code")]];
};
u24 calculatedPRGROMSize = 16384 * ((0x0100 * ($[9] & 0x0F)) * ($[7] & 12 == 8) + header.prgROMSizeBy16KiBs);
u16 PRGROM_MINIMUM_SIZE = 16384;
u8 LOWER_TWO_DIGITS = 0b11;
u32 calculatedPRGROMSize = (std::mem::size()-16-(4096*($[0x0E]&LOWER_TWO_DIGITS))) * ($[0x09]&FILLED_HIGHER_NYBBLE==0x0F)
+ (PRGROM_MINIMUM_SIZE * ((0x0100 * ($[0x09] & FILLED_HIGHER_NYBBLE)) * ($[0x07] & FILLED_NES2_FLAGS == SET_NES2_FLAGS) + header.prgROMSizeBy16KiBs)) * ($[0x09]&FILLED_HIGHER_NYBBLE!=0x0F);
fn hasOfficialHeader() {
u8 sum;
for (u8 i = 0, i < 8, i += 1) {
sum += $[(calculatedPRGROMSize - 14) + i];
}
return !sum;
fn hasFooter() {
u8 sum;
for (u8 i = 0, i < 8, i += 1) {
sum += $[(calculatedPRGROMSize - 14) + i];
}
return !sum;
};
u8 FOOTER_SIZE = 26;
u8 VECTORS_SIZE = 6;
struct PRGROM {
u8 data[calculatedPRGROMSize - 26 * hasOfficialHeader() - 6];
if (hasOfficialHeader())
OfficialHeader officialHeader;
u16 nmi;
u16 resetVector [[comment("Entry Point")]];
u16 externalIRQ;
u8 data[calculatedPRGROMSize - FOOTER_SIZE * hasFooter() - VECTORS_SIZE];
if (hasFooter())
Footer footer;
u16 nmi;
u16 resetVector [[comment("Entry Point")]];
u16 externalIRQ;
};
PRGROM prgROM @ 0x10 + sizeof(trainer);
u8 chrROM[8192 * ((0x0100 * ($[9] >> 4)) * ($[7] & 12 == 8) + header.chrROMSizeBy8KiBs)] @ addressof(prgROM) + 16384 * header.prgROMSizeBy16KiBs;
fn identifyMapper(u16 mapperValue, u8 submapperValue) {
str mapper;
str submapper;
str designation;
match (mapperValue) {
(0): mapper = "NROM";
(1): mapper = "MMC1B";
(2): mapper = "UxROM";
(3): mapper = "CNROM-32";
(4): mapper = "MMC3";
(5): mapper = "MMC5";
(6): mapper = "Front Fareast Magic Card 1/2M RAM Cartridge";
(7): mapper = "AxROM";
(8): mapper = "Front Fareast Magic Card 1/2M RAM Cartridge (Initial latch-based banking mode 4)";
(9): mapper = "MMC2";
(10): mapper = "MMC4";
(11): mapper = "Color Dreams";
(12): mapper = "(See submapper)";
(13): mapper = "CPROM";
(14): mapper = "SL-1632";
(15): mapper = "K-102xx";
(16): mapper = "Bandai FCG boards";
(17): mapper = "Front Fareast Super Magic Card RAM cartridge";
(18): mapper = "SS88006 (Jaleco)";
(19): mapper = "Namco 129/163";
(20): mapper = "Famicom Disk System";
(21): mapper = "VRC4a/VRC4c";
(22): mapper = "VRC2a";
(23): mapper = "VRC4e or VRC2b + VRC4f";
(24): mapper = "VRC6a";
(25): mapper = "VRC4d or VRC2c + VRC4b";
(26): mapper = "VRC6b";
(27): mapper = "World Hero";
(28): mapper = "Action 53";
(29): mapper = "RET-CUFROM";
(30): mapper = "UNROM 512";
(31): mapper = "NSF";
(32): mapper = "G-101";
(33): mapper = "TC0190";
(34): mapper = "(See submapper)";
(35): mapper = "J.Y. Company (8KiB WRAM)";
(36): mapper = "01-22000-400";
(37): mapper = "SMB+Tetris+NWC";
(38): mapper = "Crime Busters";
(39): mapper = "Study & Game 32-in-1";
(40): mapper = "NTDEC 27xx";
(41): mapper = "Caltron 6-in-1";
(42): mapper = "FDS -> NES Hacks";
(43): mapper = "TONY-I/YS-612";
(44): mapper = "Super Big 7-in-1";
(45): mapper = "GA23C";
(46): mapper = "Rumble Station";
(47): mapper = "Super Spike V'Ball + NWC";
(48): mapper = "TC0690";
(49): mapper = "Super HIK 4-in-1";
(50): mapper = "761214";
(51): mapper = "11-in-1 Ball Games";
(52): mapper = "Realtec 8213";
(53): mapper = "Supervision 16-in-1";
(54): mapper = "Novel Diamond 9999999-in-1";
(55): mapper = "QFJ";
(56): mapper = "KS202";
(57): mapper = "GK";
(58): mapper = "WQ";
(59): mapper = "T3H53";
(60): mapper = "Reset-based NROM-128 4-in-1";
(61): mapper = "(See submapper)";
(62): mapper = "Super 700-in-1";
(63): mapper = "(See submapper)";
(64): mapper = "RAMBO-1";
(65): mapper = "H3001";
(66): mapper = "GxROM";
(67): mapper = "Sunsoft-3";
(68): mapper = "Sunsoft-4";
(69): mapper = "Sunsoft FME-7/Sunsoft 5A/Sunsoft 5B";
(70): mapper = "Family Trainer Mat";
(71): mapper = "Camerica";
(72): mapper = "JF-17";
(73): mapper = "VRC3";
(74): mapper = "860908C";
(75): mapper = "VRC1";
(76): mapper = "NAMCOT-3446";
(77): mapper = "Napoleon Senki";
(78): mapper = "74HC161/32";
(79): mapper = "NINA-003/NINA-006";
(80): mapper = "X1-005";
(81): mapper = "N715021";
(82): mapper = "X1-017";
}
if (mapperValue == 3) {
match (submapperValue) {
(0): submapper = "Bus conflict";
(1): submapper = "No bus conflicts";
(2): submapper = "AND-type bus conflicts";
}
}
else if (mapperValue == 4) designation = "TxROM";
else if (mapperValue == 12) {
match (submapperValue) {
(0): submapper = "SL-5020B (Gouder)";
(1): submapper = "Front Fareast Magic Card 4M RAM Cartridge";
}
}
else if (mapperValue == 16) {
match (submapperValue) {
(0): submapper = "FCG-1/2 or LZ93D50";
(4): submapper = "FCG-1/2";
(5): submapper = "LZ93D50";
}
}
else if (mapperValue == 34) {
match (submapperValue) {
(0): submapper = "NINA-001/NINA-002";
(1): submapper = "BNROM";
}
}
else if (mapperValue == 40) {
match (submapperValue) {
(0): submapper = "NTDEC 2722";
(1): submapper = "NTDEC 2752";
}
}
else if (mapperValue == 61) {
match (submapperValue) {
(0): submapper = "NTDEC 0324";
(1): submapper = "NTDEC BS-N032";
(_): submapper = "GS-2017";
}
}
else if (mapperValue == 63) {
match (submapperValue) {
(0): submapper = "TH2291-3";
(1): submapper = "82AB";
}
}
std::print("Mapper: " + mapper + "(" + std::string::to_string(mapperValue) + ")");
if (submapper) std::print("Submapper: " + submapper + "(" + std::string::to_string(submapperValue) + ")");
if (designation) std::print("Designation: " + designation);
u16 CHRROM_MINIMUM_SIZE = 8192;
u24 calculatedCHRROMSize = CHRROM_MINIMUM_SIZE * ((0x0100 * ($[0x09] >> 4)) * ($[0x07] & FILLED_NES2_FLAGS == SET_NES2_FLAGS) + header.chrROMSizeBy8KiBs);
u8 chrROM[calculatedCHRROMSize] @ addressof(prgROM) + calculatedPRGROMSize;
struct MiscellaneousROMs {
if ($[0x07] & LOWER_TWO_DIGITS == ConsoleType::PlayChoice10) {
u8 instructionsROM[8192] [[hex::spec_name("INST-ROM")]];
u8 decryptionDataPROM[16];
u8 decryptionCounterOutPROM[16];
}
else if ($[0x0D] == ExtendedConsoleType::VT369) {
u8 embeddedROM[std::mem::size()-calculatedCHRROMSize-calculatedPRGROMSize-sizeof(trainer)-0x10];
}
else if (mapperValue == 355) {
u8 antiCloningROM[1024];
}
};
identifyMapper(0x0100 * ($[8] & 0x0F) + 0x10 * ($[7] & 0x0F) + header.flags.lowerMapperNybble, $[8] >> 4);
MiscellaneousROMs miscellaneousROMs[$[0x0E] & LOWER_TWO_DIGITS > 0] @ addressof(prgROM) + calculatedPRGROMSize + calculatedCHRROMSize;

View File

@@ -47,10 +47,10 @@ struct DOSHeader {
u16 initialCSValue [[hex::spec_name("e_cs")]];
u16 relocationsTablePointer [[hex::spec_name("e_lfarlc")]];
u16 overlayNumber [[hex::spec_name("e_ovno")]];
u16 reservedWords[4] [[hex::spec_name("e_res")]];
padding[8];
u16 oemIdentifier [[hex::spec_name("e_oemid")]];
u16 oemInformation [[hex::spec_name("e_oeminfo")]];
u16 otherReservedWords[10] [[hex::spec_name("e_res2")]];
padding[20];
u32 coffHeaderPointer [[hex::spec_name("e_lfanew")]];
};
@@ -1115,7 +1115,7 @@ struct Section {
} [[name(sectionsTable[currentSectionIndex-1].name)]];
Section sections[coffHeader.numberOfSections] @ coffHeader.optionalHeader.sizeOfHeaders * !sectionsTable[0].sizeOfRawData
+ sectionsTable[0].ptrRawData * sectionsTable[0].sizeOfRawData>0;
+ sectionsTable[0].ptrRawData * (sectionsTable[0].sizeOfRawData>0);
// Symbol & String Tables
enum SectionNumberType : s16 {

133
patterns/spc.hexpat Normal file
View File

@@ -0,0 +1,133 @@
#pragma author DexrnZacAttack
#pragma description SNES SPC-700 Sound File
#pragma magic [ 53 4E 45 53 2D 53 50 43 37 30 30 20 53 6F 75 6E 64 20 46 69 6C 65 20 44 61 74 61 ] @ 0x00
#pragma version 1.0.0
import std.mem;
// https://wiki.superfamicom.org/spc-and-rsn-file-format
namespace SPC {
namespace ID666 {
enum ID666Bool : u8 {
True = 26,
False = 27
};
struct TextMain {
char songTitle[32];
char gameTitle[32];
char dumperName[16];
char comments[32];
char dumpDate[11];
u24 secondsTillFadeout;
u32 fadeMs;
u8 fadeMsHigh;
char songArtist[32];
u8 defaultChannelDisabled; // inverse bool
u8 dumperEmu;
char reserved[45];
};
struct BinaryMain {
char songTitle[32];
char gameTitle[32];
char dumperName[16];
char comments[32];
char dumpDate[4];
// unused
u32;
u24;
// end unused
u24 secondsTillFadeout;
char fadeMs[4]; // why
char songArtist[32];
u8 defaultChannelDisabled; // inverse bool
u8 dumperEmu;
char reserved[46];
};
namespace Extended {
enum SubChunkType : u8 {
Header,
String,
Integer = 4
};
enum SubChunkId : u8 {
SongName = 0x01,
GameName = 0x02,
ArtistName = 0x03,
DumperName = 0x04,
DateDumped = 0x05,
EmulatorUsed = 0x06,
Comments = 0x07,
OfficialSoundtrackTitle = 0x10,
OstDisc = 0x11,
OstTrack = 0x12,
PublisherName = 0x13,
CopyrightYear = 0x14,
IntroductionLength = 0x30,
LoopLength = 0x31,
EndLength = 0x32,
FadeLength = 0x33,
MutedVoices = 0x34,
LoopCount = 0x35,
PreampLevel = 0x36
};
struct SubChunk {
std::mem::AlignTo<4>;
SubChunkId id;
SubChunkType type;
u16 dat;
if (type != SubChunkType::Header) {
if (type == SubChunkType::String) {
char data[dat];
} else {
u8 data[dat];
}
}
};
struct Chunk {
u64 off = $;
u8 type[4];
u32 size;
SubChunk sc[while($ < off + size)];
};
struct Main {
if (!std::mem::eof())
Chunk chunk;
};
}
}
struct Main {
char signature[33];
u16;
ID666::ID666Bool hasId666;
u8 versionMinor;
u16 registerPc;
u8 registerA;
u8 registerX;
u8 registerY;
u8 registerPSW;
u8 registerSpLow;
u16 registerReserved;
if (hasId666 == ID666::ID666Bool::True) {
ID666::BinaryMain id666;
}
u8 ram[65536];
u8 dspRegisters[128];
u8 unused[64];
u8 ramExtra[64];
ID666::Extended::Main id666Extended;
};
}
#ifndef SPC_USE_LIB
SPC::Main spc @ 0x00;
#endif

View File

@@ -98,12 +98,13 @@ int main(int argc, char **argv) {
if (missingComments)
return EXIT_FAILURE;
if (!runtime.executeString(patternFile.readString(), "<Source Code>")) {
auto exitCode = runtime.executeString(patternFile.readString(), "<Source Code>");
if (exitCode != 0) {
if (const auto &evalError = runtime.getEvalError(); evalError.has_value()) {
fmt::println("{}:{} {}", evalError->line, evalError->column, evalError->message);
}
return EXIT_FAILURE;
return exitCode;
}
if (!runtime.getPatterns().empty()) {

View File

@@ -95,8 +95,9 @@ int main(int argc, char **argv) {
// Execute pattern
fmt::println("Executing pattern {} using test file {}", patternName, wolv::util::toUTF8String(testFilePath.filename()));
if (!runtime.executeString(patternFile.readString(), "<Source Code>")) {
fmt::println("Error when executing pattern!");
auto exitCode = runtime.executeString(patternFile.readString(), "<Source Code>");
if (exitCode != 0) {
fmt::println("Non-zero exit code returned from execution. (Exit Code: {})!", exitCode);
if (const auto &compileErrors = runtime.getCompileErrors(); !compileErrors.empty()) {
for (const auto &error : compileErrors) {
@@ -106,7 +107,7 @@ int main(int argc, char **argv) {
fmt::println("{}:{} {}", evalError->line, evalError->column, evalError->message);
}
return EXIT_FAILURE;
return exitCode;
}
for (const auto &pattern : runtime.getPatterns()) {

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 B

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.