mirror of
https://github.com/WerWolv/ImHex-Patterns.git
synced 2026-03-27 23:37:04 -05:00
patterns: Improvements to NES & IPS, add SNES, NSF, NSFe (#455)
* 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 --------- Co-authored-by: Nik <werwolv98@gmail.com>
This commit is contained in:
@@ -123,7 +123,9 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi
|
||||
| NBT | | [`patterns/nbt.hexpat`](patterns/nbt.hexpat) | Minecraft NBT format |
|
||||
| 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) | .nes file format |
|
||||
| nes | | [`patterns/nes.hexpat`](patterns/nes.hexpat) | Nintendo Entertainment System ROM |
|
||||
| NSF | | [`patterns/nsf.hexpat`](patterns/nsf.hexpat) | NES Sound Format |
|
||||
| NSFe | | [`patterns/nsfe.hexpat`](patterns/nsfe.hexpat) | NES Sound Format extended |
|
||||
| NotepadCache | | [`patterns/notepad-cache.hexpat`](patterns/notepad-cache.hexpat) | Windows Notepad Cache |
|
||||
| NotepadStateFile | | [`patterns/notepad-state.hexpat`](patterns/notepad-state.hexpat) | Windows Notepad .bin State files |
|
||||
| NotepadWindowState | | [`patterns/notepadwindowstate.hexpat`](patterns/notepadwindowstate.hexpat) | Windows 11 Notepad - Window State .bin file |
|
||||
@@ -169,6 +171,7 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi
|
||||
| SHR | | [`patterns/SHR.hexpat`](patterns/SHR.hexpat) | Apple IIgs Super Hi-Res (SHR) + PaintWorks Animation (ANI) |
|
||||
| shx | | [`patterns/shx.hexpat`](patterns/shx.hexpat) | ESRI index file |
|
||||
| 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 |
|
||||
| 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 |
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
#pragma endian big
|
||||
|
||||
import std.mem;
|
||||
import std.string;
|
||||
|
||||
struct Hunk {
|
||||
@@ -23,7 +22,7 @@ struct IPS {
|
||||
char signature[5];
|
||||
Hunk hunks[while($ < std::mem::size() - (3 + 3 * (std::mem::read_string(std::mem::size()-3, 3) != "EOF")))];
|
||||
char eof[3];
|
||||
u24 truncatedSize[3+(std::mem::read_string(std::mem::size()-3, 3) != "EOF")>3];
|
||||
u24 truncatedSize[std::mem::read_string(std::mem::size()-3, 3) != "EOF"];
|
||||
};
|
||||
|
||||
IPS ips @ 0x00;
|
||||
|
||||
@@ -145,7 +145,7 @@ struct NES2Attributes {
|
||||
}
|
||||
MiscellaneousROMs miscellaneousROMs;
|
||||
DefaultExpansionDevice defaultExpansionDevice;
|
||||
};
|
||||
} [[inline]];
|
||||
|
||||
fn renderEOF(str string) {
|
||||
return "\"NES<EOF>\"";
|
||||
@@ -243,7 +243,7 @@ struct OfficialHeader {
|
||||
u8 complementaryChecksum [[hex::spec_name("Checksum for characterChecksum~makerID")]];
|
||||
};
|
||||
|
||||
u24 calculatedPRGROMSize = 16384 * ((0x0100 * $[9] & 0x0F) * ($[7] & 12 == 8) + header.prgROMSizeBy16KiBs);
|
||||
u24 calculatedPRGROMSize = 16384 * ((0x0100 * ($[9] & 0x0F)) * ($[7] & 12 == 8) + header.prgROMSizeBy16KiBs);
|
||||
|
||||
fn hasOfficialHeader() {
|
||||
u8 sum;
|
||||
@@ -263,4 +263,145 @@ struct PRGROM {
|
||||
};
|
||||
|
||||
PRGROM prgROM @ 0x10 + sizeof(trainer);
|
||||
u8 chrROM[8192 * ((0x0100 * $[9] >> 4) * ($[7] & 12 == 8) + header.chrROMSizeBy8KiBs)] @ addressof(prgROM) + 16384 * header.prgROMSizeBy16KiBs;
|
||||
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);
|
||||
};
|
||||
identifyMapper(0x0100 * ($[8] & 0x0F) + 0x10 * ($[7] & 0x0F) + header.flags.lowerMapperNybble, $[8] >> 4);
|
||||
|
||||
109
patterns/nsf.hexpat
Normal file
109
patterns/nsf.hexpat
Normal file
@@ -0,0 +1,109 @@
|
||||
#pragma author gmestanley
|
||||
#pragma description NES Sound Format file
|
||||
|
||||
import std.string;
|
||||
|
||||
struct ChunkMetadata {
|
||||
u32 length;
|
||||
char ID[4];
|
||||
} [[inline]];
|
||||
|
||||
struct TimeChunkData {
|
||||
u32 trackLengths[while($<addressof(parent.metadata)+6+parent.metadata.length)];
|
||||
} [[inline]];
|
||||
|
||||
fn formatMetadataName(str string) {
|
||||
return "\"" + std::string::substr(string, 0, sizeof(string)-1) + "\"";
|
||||
};
|
||||
|
||||
struct Name {
|
||||
char trackName[] [[format("formatMetadataName")]];
|
||||
};
|
||||
|
||||
struct TlblChunkData {
|
||||
Name trackNames[while($<addressof(parent.metadata)+6+parent.metadata.length)];
|
||||
} [[inline]];
|
||||
|
||||
struct AuthChunkData {
|
||||
char gameTitle[] [[format("formatMetadataName")]];
|
||||
char songwriting[] [[format("formatMetadataName")]];
|
||||
char copyright[] [[format("formatMetadataName")]];
|
||||
char ripper[] [[format("formatMetadataName")]];
|
||||
} [[inline]];
|
||||
|
||||
struct TextChunkData {
|
||||
char text[parent.metadata.length];
|
||||
} [[inline]];
|
||||
|
||||
struct Chunk {
|
||||
ChunkMetadata metadata;
|
||||
if (metadata.ID == "time") TimeChunkData timeChunkData;
|
||||
else if (metadata.ID == "text") TextChunkData textChunkData;
|
||||
else if (metadata.ID == "tlbl") TlblChunkData tlblChunkData;
|
||||
else if (metadata.ID == "auth") AuthChunkData authChunkData;
|
||||
else u8 data[metadata.length];
|
||||
};
|
||||
|
||||
bitfield NSF2Flags {
|
||||
padding : 4;
|
||||
irq : 1;
|
||||
nonReturningInitNotUsed : 1;
|
||||
playSubroutineNotUsed : 1;
|
||||
playbackNSFeChunk : 1;
|
||||
};
|
||||
|
||||
bitfield Region {
|
||||
isPAL : 1;
|
||||
palNTSCDual : 1;
|
||||
};
|
||||
|
||||
enum ExtraSoundChipSupport : u8 {
|
||||
None,
|
||||
VRC6,
|
||||
VRC7,
|
||||
FDS = 4,
|
||||
MMC5 = 8,
|
||||
Namco163 = 16,
|
||||
Sunsoft5B = 32,
|
||||
VTxx = 64
|
||||
};
|
||||
|
||||
fn formatName(str string) {
|
||||
for (u8 nameIndex = 0, nameIndex < 32, nameIndex += 1) {
|
||||
if (!$[$+nameIndex] || nameIndex == 31)
|
||||
return "\"" + std::string::substr(string, 0, nameIndex) + "\"";
|
||||
}
|
||||
};
|
||||
|
||||
fn renderEOF(str value) {
|
||||
return "\"NESM<EOF>\"";
|
||||
};
|
||||
|
||||
struct Header {
|
||||
char signature[5] [[format("renderEOF")]];
|
||||
u8 version;
|
||||
u8 songAmount;
|
||||
u8 startingSong;
|
||||
u16 dataLoadAddress;
|
||||
u16 dataInitAddress;
|
||||
u16 dataPlayAddress;
|
||||
char gameName[32] [[format("formatName")]];
|
||||
char songwriting[32] [[format("formatName")]];
|
||||
char copyrightHolder[32] [[format("formatName")]];
|
||||
u16 ntscPlaySpeed;
|
||||
u8 bankswitchInitValues[8];
|
||||
u16 palPlaySpeed;
|
||||
Region region;
|
||||
ExtraSoundChipSupport extraSoundChipSupport;
|
||||
NSF2Flags nsf2flags;
|
||||
u24 dataLength;
|
||||
};
|
||||
|
||||
Header header @ 0x00;
|
||||
|
||||
struct Chunks {
|
||||
if (header.dataLength)
|
||||
NSFEChunk chunks[while($<std::mem::size())];
|
||||
};
|
||||
|
||||
Chunks metadata @ 0x80+header.dataLength;
|
||||
48
patterns/nsfe.hexpat
Normal file
48
patterns/nsfe.hexpat
Normal file
@@ -0,0 +1,48 @@
|
||||
#pragma author gmestanley
|
||||
#pragma description NSFe file format
|
||||
|
||||
import std.string;
|
||||
|
||||
struct ChunkMetadata {
|
||||
u32 length;
|
||||
char ID[4];
|
||||
} [[inline]];
|
||||
|
||||
struct TimeChunkData {
|
||||
u32 trackLengths[while($<addressof(parent.metadata)+6+parent.metadata.length)];
|
||||
} [[inline]];
|
||||
|
||||
fn formatMetadataName(str string) {
|
||||
return "\"" + std::string::substr(string, 0, sizeof(string)-1) + "\"";
|
||||
};
|
||||
|
||||
struct Name {
|
||||
char trackName[] [[format("formatMetadataName")]];
|
||||
};
|
||||
|
||||
struct TlblChunkData {
|
||||
Name trackNames[while($<addressof(parent.metadata)+6+parent.metadata.length)];
|
||||
} [[inline]];
|
||||
|
||||
struct AuthChunkData {
|
||||
char gameTitle[] [[format("formatMetadataName")]];
|
||||
char songwriting[] [[format("formatMetadataName")]];
|
||||
char copyright[] [[format("formatMetadataName")]];
|
||||
char ripper[] [[format("formatMetadataName")]];
|
||||
} [[inline]];
|
||||
|
||||
struct TextChunkData {
|
||||
char text[parent.metadata.length];
|
||||
} [[inline]];
|
||||
|
||||
struct Chunk {
|
||||
ChunkMetadata metadata;
|
||||
if (metadata.ID == "time") TimeChunkData timeChunkData;
|
||||
else if (metadata.ID == "text") TextChunkData textChunkData;
|
||||
else if (metadata.ID == "tlbl") TlblChunkData tlblChunkData;
|
||||
else if (metadata.ID == "auth") AuthChunkData authChunkData;
|
||||
else u8 data[metadata.length];
|
||||
};
|
||||
|
||||
char signature[4] @ 0x00;
|
||||
Chunk chunks[while($<std::mem::size())] @ 0x04;
|
||||
@@ -5,11 +5,33 @@
|
||||
#pragma MIME application/x-msdownload
|
||||
#pragma MIME application/vnd.microsoft.portable-executable
|
||||
|
||||
import hex.dec;
|
||||
import std.core;
|
||||
import std.string;
|
||||
import type.guid;
|
||||
import type.time;
|
||||
|
||||
fn formatNullTerminatedString(str string) {
|
||||
return "\"" + std::string::substr(string, 0, std::string::length(string)-1) + "\"";
|
||||
};
|
||||
|
||||
s16 dosMessageOffset;
|
||||
|
||||
struct DOSProgramData {
|
||||
u8 code[while($ < addressof(this) + dosMessageOffset)];
|
||||
// $ is the character that ends text on DOS
|
||||
char message[while(std::mem::read_string($-1, 1) != "$")] @ addressof(this) + dosMessageOffset [[format("formatNullTerminatedString")]];
|
||||
} [[hex::spec_name("e_program")]];
|
||||
|
||||
fn finddosmessage() {
|
||||
for (u8 cursor = $, cursor < $+16, cursor += 1) {
|
||||
if ($[cursor] == 0xBA) { // Message offset instruction
|
||||
dosMessageOffset = std::mem::read_unsigned(cursor+1, 2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct DOSHeader {
|
||||
char signature[2] [[hex::spec_name("e_magic")]];
|
||||
u16 extraPageSize [[hex::spec_name("e_cblp")]];
|
||||
@@ -32,33 +54,15 @@ struct DOSHeader {
|
||||
u32 coffHeaderPointer [[hex::spec_name("e_lfanew")]];
|
||||
};
|
||||
|
||||
u16 dosMessageOffset;
|
||||
|
||||
fn isstubdata(char c) {
|
||||
return c == 0x0D || c == 0x0A || c == '$';
|
||||
};
|
||||
|
||||
struct DOSStub {
|
||||
u8 code[while($ < addressof(this) + dosMessageOffset)];
|
||||
char message[while(!isstubdata(std::mem::read_unsigned($, 1)))];
|
||||
char data[while(std::mem::read_string($-1, 1) != "$")];
|
||||
} [[hex::spec_name("e_program")]];
|
||||
|
||||
fn finddosmessage() {
|
||||
for (u8 i = $, i < $+16, i += 1) {
|
||||
if (std::mem::read_unsigned(i, 1) == 0xBA) { // Message offset instruction
|
||||
dosMessageOffset = std::mem::read_unsigned(i+1, 2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct PEHeader {
|
||||
DOSHeader dosHeader;
|
||||
finddosmessage();
|
||||
if (!dosMessageOffset)
|
||||
u8 dosStub[while(std::mem::read_unsigned($, 4))] [[hex::spec_name("e_program")]];
|
||||
else DOSStub dosStub;
|
||||
/* Some newer executables have garbage data instead. Four 00
|
||||
characters is when 00 (null) stops being part of it instead
|
||||
of being the threshold, so it can be used as a limit */
|
||||
u8 dosProgramData[while(std::mem::read_unsigned($, 5))] [[hex::spec_name("e_program")]];
|
||||
else DOSProgramData dosProgramData;
|
||||
};
|
||||
|
||||
PEHeader peHeader @ 0x00;
|
||||
@@ -254,7 +258,7 @@ enum AlignmentType : u8 {
|
||||
};
|
||||
|
||||
fn formatAlignmentBits(u8 value) {
|
||||
if (value > 0) {
|
||||
if (value) {
|
||||
AlignmentType enumValue = value;
|
||||
return enumValue;
|
||||
}
|
||||
@@ -325,10 +329,6 @@ fn wordsize() {
|
||||
return std::mem::read_unsigned(addressof(coffHeader.optionalHeader.magic), 2) / 0x41;
|
||||
};
|
||||
|
||||
fn formatNullTerminatedString(str string) {
|
||||
return "\"" + std::string::substr(string, 0, std::string::length(string)-1) + "\"";
|
||||
};
|
||||
|
||||
// Exception Table
|
||||
bitfield FunctionBitfield {
|
||||
prologLength : 8;
|
||||
@@ -408,8 +408,15 @@ bitfield OrdinalFlagByte {
|
||||
flag : 1 [[name("ordinalFlag")]];
|
||||
};
|
||||
|
||||
fn formatMangledString(str string) {
|
||||
return hex::dec::demangle(formatNullTerminatedString(string));
|
||||
};
|
||||
|
||||
struct ImportsName {
|
||||
u16 hint;
|
||||
if (std::mem::read_string($, 1) == "?")
|
||||
char name[] [[format("formatMangledString")]];
|
||||
else
|
||||
char name[] [[format("formatNullTerminatedString")]];
|
||||
if ($ % 2 == 1) { u8 pad; }
|
||||
};
|
||||
@@ -679,14 +686,14 @@ struct VersionEntryHeader {
|
||||
u16 valueLength;
|
||||
u16 type;
|
||||
char16 key[] [[format("formatNullTerminatedString16")]];
|
||||
padding[while(std::mem::read_unsigned($, 1) == 0 && $ < addressof(key)+sizeof(key)+5)];
|
||||
padding[while(!$[$] && $ < addressof(key)+sizeof(key)+5)];
|
||||
};
|
||||
|
||||
struct StringInfo {
|
||||
VersionEntryHeader stringInfoHeader;
|
||||
if (stringInfoHeader.valueLength > 0) {
|
||||
char16 string[] [[format("formatNullTerminatedString16")]];
|
||||
padding[while(std::mem::read_unsigned($, 1) == 0)];
|
||||
padding[while(!$[$])];
|
||||
}
|
||||
};
|
||||
|
||||
@@ -702,7 +709,7 @@ struct VersionEntry {
|
||||
}
|
||||
else {
|
||||
u8 value[header.valueLength];
|
||||
padding[while(std::mem::read_unsigned($, 1) == 0)];
|
||||
padding[while(!$[$])];
|
||||
}
|
||||
};
|
||||
|
||||
@@ -723,15 +730,15 @@ struct Version {
|
||||
|
||||
// * Resources Using TrueChar
|
||||
fn displayTrueChar(u8 value) {
|
||||
char chr = char(value);
|
||||
str notation = "0x";
|
||||
if (value < 0x10)
|
||||
notation += "0";
|
||||
if (value == 0)
|
||||
return "'␀' (" + std::format(notation + "{:X}", value) + ")";
|
||||
else
|
||||
return "'" + char(value) + "' (" + std::format(notation + "{:X}", value) + ")";
|
||||
if (value < 0x10) notation += "0";
|
||||
if (!value) chr = "␀";
|
||||
return "'" + chr + "' (" + std::format(notation + "{:X}", value) + ")";
|
||||
};
|
||||
|
||||
using TrueChar = u8 [[format("displayTrueChar")]];
|
||||
|
||||
// Resource Table
|
||||
enum ResourceID : u32 {
|
||||
Cursor = 0x01,
|
||||
@@ -749,8 +756,6 @@ enum ResourceID : u32 {
|
||||
};
|
||||
ResourceID resourceIDType;
|
||||
|
||||
using TrueChar = u8 [[format("displayTrueChar")]];
|
||||
|
||||
struct DataEntry {
|
||||
u32 dataRVA;
|
||||
u32 size;
|
||||
@@ -798,7 +803,7 @@ bitfield OffsetField {
|
||||
};
|
||||
|
||||
struct DataField {
|
||||
if (std::mem::read_unsigned($+3, 1) >= 0x80) {
|
||||
if ($[$+3] >= 0x80) {
|
||||
OffsetField offsetToData;
|
||||
ResourceDirectory directory @ coffHeader.optionalHeader.directories[2].rva - relativeVirtualDifference() + offsetToData.offset;
|
||||
}
|
||||
@@ -972,8 +977,7 @@ enum DebugType : u32 {
|
||||
OmapToSRC,
|
||||
OmapFromSRC,
|
||||
Borland,
|
||||
Reserved10,
|
||||
CLSID,
|
||||
CLSID = 11,
|
||||
REPRO = 16,
|
||||
ExtendedDLLCharacteristics = 20
|
||||
};
|
||||
@@ -1056,7 +1060,7 @@ fn noDataDirectories() {
|
||||
return true;
|
||||
};
|
||||
|
||||
fn clearBoolArray() {
|
||||
fn clearDirectoryChecks() {
|
||||
for (u32 i = 0, i < coffHeader.optionalHeader.numberOfRVAsAndSizes, i += 1)
|
||||
dataDirectoryInSection[i] = false;
|
||||
};
|
||||
@@ -1098,7 +1102,7 @@ struct Section {
|
||||
DelayedImportsTable delayedImportTable @ coffHeader.optionalHeader.directories[13].rva - relativeVirtualDifference();
|
||||
}
|
||||
}
|
||||
clearBoolArray();
|
||||
clearDirectoryChecks();
|
||||
|
||||
LineNumber lineNumbers[sectionsTable[currentSectionIndex].numberOfLineNumbers] @ sectionsTable[currentSectionIndex].ptrLineNumbers;
|
||||
|
||||
@@ -1110,7 +1114,8 @@ struct Section {
|
||||
currentSectionIndex += 1; // Make the current section index the next section's index
|
||||
} [[name(sectionsTable[currentSectionIndex-1].name)]];
|
||||
|
||||
Section sections[coffHeader.numberOfSections] @ sectionsTable[0].ptrRawData;
|
||||
Section sections[coffHeader.numberOfSections] @ coffHeader.optionalHeader.sizeOfHeaders * !sectionsTable[0].sizeOfRawData
|
||||
+ sectionsTable[0].ptrRawData * sectionsTable[0].sizeOfRawData>0;
|
||||
|
||||
// Symbol & String Tables
|
||||
enum SectionNumberType : s16 {
|
||||
|
||||
110
patterns/snes.hexpat
Normal file
110
patterns/snes.hexpat
Normal file
@@ -0,0 +1,110 @@
|
||||
#pragma author gmestanley
|
||||
#pragma description Super Nintendo Entertainment System ROM header
|
||||
#pragma sources snes.nesdev.org/wiki/ROM_header CPU_vectors en.wikibooks.org/wiki/Super_NES_Programming/SNES_memory_map
|
||||
|
||||
import std.string;
|
||||
|
||||
u24 headerPosition = 0x7FC0;
|
||||
|
||||
fn calculateHeaderPosition() {
|
||||
if (std::mem::size() > 0x20000 && std::mem::size() < 0x600000) headerPosition += 0x8000;
|
||||
else if (std::mem::size() >= 0x600000) headerPosition += 0x400000;
|
||||
};
|
||||
calculateHeaderPosition();
|
||||
|
||||
enum ChipsetSubtype : u8 {
|
||||
SPC7110,
|
||||
ST01x,
|
||||
ST018,
|
||||
CX4
|
||||
};
|
||||
|
||||
struct ExpandedHeader {
|
||||
char makerID[2];
|
||||
char gameID[4];
|
||||
padding[6];
|
||||
u8 expansionFlashSize [[comment("1 << N")]];
|
||||
u8 expansionRAMSize [[comment("1 << N")]];
|
||||
u8 specialVersion;
|
||||
ChipsetSubtype chipsetSubtype;
|
||||
};
|
||||
|
||||
struct ConditionalStruct {
|
||||
if ($[headerPosition+0x1A] == 0x33) ExpandedHeader expandedHeader @ headerPosition - 0x10;
|
||||
else if (!$[headerPosition+0x14]) ChipsetSubtype chipsetSubtype @ headerPosition - 1;
|
||||
} [[inline]];
|
||||
|
||||
ConditionalStruct conditionalStruct @ $;
|
||||
|
||||
enum MappingMode : u8 {
|
||||
LoROM,
|
||||
HiROM,
|
||||
ExHiROM = 5
|
||||
};
|
||||
|
||||
fn formatMappingMode(u8 value) {
|
||||
MappingMode enumValue = value;
|
||||
return enumValue;
|
||||
};
|
||||
|
||||
bitfield ROMType {
|
||||
mappingMode : 4 [[format("formatMappingMode")]];
|
||||
speed : 1;
|
||||
unknown : 1;
|
||||
};
|
||||
|
||||
enum CoprocessorType : u8 {
|
||||
DSP,
|
||||
GSU,
|
||||
OBC1,
|
||||
SA1,
|
||||
SDD1,
|
||||
SRTC,
|
||||
Other = 0x0E,
|
||||
Custom
|
||||
};
|
||||
|
||||
fn formatExtraHardwareType(u8 value) {
|
||||
str valueMeaning = " (ROM";
|
||||
if (!value) valueMeaning += " only";
|
||||
else if (value) {
|
||||
if (value > 3) valueMeaning += " + coprocessor";
|
||||
if (value != 3 || value != 6) valueMeaning += " + RAM";
|
||||
if (value == 2 || value > 5) valueMeaning += " + battery";
|
||||
}
|
||||
return std::string::to_string(value) + valueMeaning + ")";
|
||||
};
|
||||
|
||||
fn formatCoprocessorType(u8 value) {
|
||||
CoprocessorType enumValue = value;
|
||||
return enumValue;
|
||||
};
|
||||
|
||||
bitfield ExtraHardware {
|
||||
extraHardwareType : 4 [[format("formatExtraHardwareType")]];
|
||||
coprocessorType : 4 [[format("formatCoprocessorType")]];
|
||||
};
|
||||
|
||||
enum Country : u8 {
|
||||
NTSC = 1,
|
||||
PAL
|
||||
};
|
||||
|
||||
struct Header {
|
||||
char title[21];
|
||||
ROMType romType;
|
||||
ExtraHardware extraHardware;
|
||||
u8 romSize [[comment("1 << N, rounded up")]];
|
||||
u8 ramSize [[comment("1 << N")]];
|
||||
Country country;
|
||||
u8 developerID;
|
||||
u8 romVersion;
|
||||
u16 checksumComplement;
|
||||
u16 checksum;
|
||||
padding[4];
|
||||
u16 vectors[6];
|
||||
padding[4];
|
||||
u16 emulationModeVectors[6];
|
||||
};
|
||||
|
||||
Header header @ headerPosition;
|
||||
Reference in New Issue
Block a user