Any suggestions on improvement are welcome
Last active
May 7, 2025 11:43
-
-
Save playday3008/0c8ba916ba3b1c4f52654db6e3f85109 to your computer and use it in GitHub Desktop.
PBP-files pattern for ImHex, supports: EBOOT, PBOOT, PARAM, and maybe more
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma author PlayDay | |
#pragma description PlayStation Boot Package (PBP) | |
#pragma magic [ 00 50 42 50 ] @ 0x00 | |
#pragma endian little | |
//#pragma debug // Most likely will cause OOM of whole system | |
/** | |
* # PlayStation Boot Package (PBP) file format | |
* | |
* ## TODO | |
* | |
* - `~SCE` structure | |
* - `~PSP` structure/decryption/unpacking | |
* - `PSAR` structure/decryption/unpacking | |
* | |
* ## External libraries | |
* | |
* - [ELF](https://github.com/WerWolv/ImHex-Patterns/blob/master/patterns/elf.hexpat) | |
* - Slightly modified with fixes and support for PS files | |
* - [PNG](https://github.com/WerWolv/ImHex-Patterns/blob/master/patterns/png.hexpat) | |
* - [WAV](https://github.com/WerWolv/ImHex-Patterns/blob/master/patterns/wav.hexpat) | |
* | |
* ## References | |
* | |
* - [PSPDEV](https://github.com/pspdev) | |
* - [PSPSDK](https://github.com/pspdev/pspsdk) | |
* - [PSPLINK](https://github.com/pspdev/psplinkusb) | |
* - [GNU GDB](https://github.com/pspdev/gdb) | |
* - [GNU Binutils](https://github.com/pspdev/binutils-gdb) | |
* - [GNU Insight](https://github.com/pspdev/insight) | |
* - [PRXTool](https://github.com/pspdev/prxtool) | |
* - [EBOOT Signer](https://github.com/pspdev/ebootsigner) | |
* - [uOFW](https://github.com/uofw/uofw) | |
* - [PSDevWiki](https://www.psdevwiki.com) | |
* - [PSP](https://www.psdevwiki.com/psp) | |
* - [PS3](https://www.psdevwiki.com/ps3) | |
* - [PS Vita](https://www.psdevwiki.com/vita) | |
* - [PPSSPP](https://github.com/hrydgard/ppsspp) | |
* - [pspdecrypt](https://github.com/hrydgard/pspdecrypt/tree/add-sce-support) | |
* - Official PSP SDK | |
* | |
*/ | |
import hex.core; | |
import std.core; | |
import std.ptr; | |
import std.mem; | |
import std.math; | |
import std.sys; | |
import std.io; | |
import std.string; | |
import type.base; | |
import type.bcd; | |
import type.magic; | |
/// Copied from `png.hexpat` | |
namespace PNG { | |
struct header_t { | |
u8 highBitByte; | |
char signature[3]; | |
char dosLineEnding[2]; | |
char dosEOF; | |
char unixLineEnding; | |
}; | |
struct actl_t { | |
u32 frames [[comment("Total № of frames in animation")]]; | |
u32 plays [[comment("№ of times animation will loop")]]; | |
} [[comment("Animation control chunk"), name("acTL")]]; | |
enum ColorType: u8 { | |
Grayscale = 0x0, | |
RGBTriple = 0x2, | |
Palette, | |
GrayscaleAlpha, | |
RGBA = 0x6 | |
}; | |
enum Interlacing: u8 { | |
None, | |
Adam7 | |
}; | |
struct ihdr_t { | |
u32 width [[comment("Image width")]]; | |
u32 height [[comment("Image height")]]; | |
u8 bit_depth; | |
ColorType color_type [[comment("PNG Image Type")]]; | |
u8 compression_method [[comment("Only 0x0 = zlib supported by most")]]; | |
u8 filter_method [[comment("Only 0x0 = adaptive supported by most")]]; | |
Interlacing interlacing; | |
}; | |
enum sRGB: u8 { | |
Perceptual = 0x0, | |
RelativeColorimetric, | |
Saturation, | |
AbsoluteColorimetric | |
}; | |
enum Unit: u8 { | |
Unknown, | |
Meter | |
}; | |
struct phys_t { | |
u32 ppu_x [[comment("Pixels per unit, X axis")]]; | |
u32 ppu_y [[comment("Pixels per unit, Y axis")]]; | |
Unit unit; | |
}; | |
enum BlendOp: u8 { | |
Source = 0x0, | |
Over | |
}; | |
enum DisposeOp: u8 { | |
None = 0x0, | |
Background, | |
Previous | |
}; | |
struct fctl_t { | |
u32 sequence_no [[comment("Sequence №")]]; | |
u32 width [[comment("Frame width")]]; | |
u32 height; | |
u32 xoff; | |
u32 yoff; | |
u16 delay_num; | |
u16 delay_den; | |
DisposeOp dispose_op; | |
BlendOp blend_op; | |
}; | |
struct fdat_t { | |
u32 sequence_no; | |
}; | |
fn text_len() { | |
u64 len = parent.parent.length - ($ - addressof(parent.keyword)); | |
return len; | |
}; | |
struct itxt_t { | |
char keyword[]; | |
u8 compression_flag; | |
u8 compression_method; | |
char language_tag[]; | |
char translated_keyword[]; | |
char text[PNG::text_len()]; | |
}; | |
struct ztxt_t { | |
char keyword[]; | |
u8 compression_method; | |
char text[PNG::text_len()]; | |
}; | |
struct text_t { | |
char keyword[]; | |
char text[PNG::text_len()]; | |
}; | |
struct iccp_t { | |
char keyword[]; | |
u8 compression_method; | |
u8 compressed_profile[PNG::text_len()]; | |
}; | |
struct palette_entry_t { | |
u24 color; | |
} [[inline]]; | |
struct chrm_t { | |
u32 white_point_x; | |
u32 white_point_y; | |
u32 red_x; | |
u32 red_y; | |
u32 green_x; | |
u32 green_y; | |
u32 blue_x; | |
u32 blue_y; | |
}; | |
struct time_t { | |
u16 year; | |
u8 month; | |
u8 day; | |
u8 hour; | |
u8 minute; | |
u8 second; | |
}; | |
struct chunk_t { | |
u32 length [[color("17BECF")]]; | |
char name[4]; | |
#define IHDR_k "IHDR" | |
#define PLTE_k "PLTE" | |
#define sRGB_k "sRGB" | |
#define pHYs_k "pHYs" | |
#define iTXt_k "iTXt" | |
#define tEXt_k "tEXt" | |
#define zTXt_k "zTXt" | |
#define IDAT_k "IDAT" | |
#define IEND_k "IEND" | |
#define gAMA_k "gAMA" | |
#define iCCP_k "iCCP" | |
#define acTL_k "acTL" | |
#define fdAT_k "fdAT" | |
#define fcTL_k "fcTL" | |
#define cHRM_k "cHRM" | |
#define tIME_k "tIME" | |
if (name == IHDR_k) { | |
ihdr_t ihdr [[comment("Image Header chunk"), name("IHDR")]]; | |
} else if (name == PLTE_k) { | |
palette_entry_t entries[length / 3]; | |
} else if (name == sRGB_k) { | |
sRGB srgb; | |
} else if (name == pHYs_k) { | |
phys_t phys; | |
} else if (name == acTL_k) { | |
actl_t actl [[comment("Animation control chunk")]]; | |
} else if (name == fcTL_k) { | |
fctl_t fctl [[comment("Frame control chunk")]]; | |
} else if (name == iTXt_k) { | |
itxt_t text; | |
} else if (name == gAMA_k) { | |
u32 gamma [[name("image gamma"), comment("4 byte unsigned integer representing gamma times 100000")]]; | |
} else if (name == iCCP_k) { | |
iccp_t iccp; | |
} else if (name == tEXt_k) { | |
text_t text; | |
} else if (name == zTXt_k) { | |
ztxt_t text; | |
} else if (name == iCCP_k) { | |
iccp_t iccp; | |
} else if (name == fdAT_k) { | |
fdat_t fdat [[comment("Frame data chunk")]]; | |
u8 data[length-sizeof(u32)]; | |
} else if (name == cHRM_k) { | |
chrm_t chrm; | |
} else if (name == tIME_k) { | |
time_t time; | |
} else { | |
u8 data[length]; | |
} | |
u32 crc; | |
} [[name(this.name)]]; | |
struct chunk_set { | |
chunk_t chunks[while(std::mem::read_string($ + 4, 4) != "IEND")] [[inline]]; | |
} [[inline]]; | |
struct Chunks { | |
chunk_t ihdr_chunk [[comment("PNG Header chunk")]]; | |
chunk_set set [[comment("PNG Chunks"), name("Chunks"), inline]]; | |
chunk_t iend_chunk [[comment("Image End Chunk")]]; | |
}; | |
} | |
/// Wrapper for PNG | |
struct PNG<auto Size> { | |
u8 visualizer[Size] @ $ [[sealed, hex::visualize("image", this)]]; | |
be PNG::header_t header [[comment("PNG file signature"), name("Signature")]]; | |
be PNG::Chunks chunks [[name("Chunks")]]; | |
}; | |
/// Copied from `wav.hexpat` | |
namespace WAV { | |
struct RiffHeader { | |
char ckID[4] [[comment("Container Signature"), name("RIFF Header Signature")]]; | |
u32 ckSize [[comment("Size of RIFF Header"), name("RIFF Chunk Size")]]; | |
char format[4] [[comment("RIFF format"), name("WAVE Header Signature")]]; | |
}; | |
struct WaveChunk { | |
char chunkId[4]; | |
u32 chunkSize; | |
}; | |
enum WaveFormatType : u16 { | |
Unknown, | |
PCM, | |
MS_ADPCM, | |
IEEEFloatingPoint, | |
ALAW = 6, | |
MULAW, | |
IMA_ADPCM = 0x11, | |
GSM610 = 0x31, | |
MPEG = 0x50, | |
MPEGLAYER3 = 0x55, | |
}; | |
struct WaveFormat { | |
WaveFormatType formatTag; | |
u16 channels; | |
u32 samplesPerSec [[comment("Sample Frequency")]]; | |
u32 avgBytesPerSec [[comment("BPS - Used to estimate buffer size")]]; | |
u16 blockAlign; | |
}; | |
struct WaveFormatEx { | |
u16 bitsPerSample; | |
u16 extendedDataSize; | |
}; | |
struct WaveFormatExDummy { | |
u16 bitsPerSample; | |
u16 extendedDataSize; | |
u8 extendedData[extendedDataSize]; | |
}; | |
struct WaveFormatPCM { | |
u16 bitsPerSample; | |
}; | |
struct WaveMSADPCMCoefSet { | |
s16 coef1; | |
s16 coef2; | |
}; | |
struct WaveFormatMSADPCM : WaveFormatEx { | |
u16 samplesPerBlock; | |
u16 numCoef; | |
WaveMSADPCMCoefSet coef[numCoef]; | |
}; | |
bitfield WaveMPEGLayer { | |
Layer1 : 1; | |
Layer2 : 1; | |
Layer3 : 1; | |
padding : 13; | |
}; | |
bitfield WaveMPEGMode { | |
Stereo : 1; | |
JointStereo : 1; | |
DualChannel : 1; | |
SingleChannel : 1; | |
padding : 12; | |
}; | |
bitfield WaveMPEGFlags { | |
PrivateBit : 1; | |
Copyright : 1; | |
OriginalHome : 1; | |
ProtectionBit : 1; | |
IdMPEG1 : 1; | |
padding : 11; | |
}; | |
struct WaveFormatIEEEFloatingPoint : WaveFormatPCM { | |
}; | |
struct WaveFormatMPEG : WaveFormatEx { | |
WaveMPEGLayer headLayersUsed; | |
u32 headBitrate; | |
WaveMPEGMode headMode; | |
u16 headModeExt; | |
u16 headEmphasis; | |
WaveMPEGFlags headFlags; | |
u32 PTSLow; | |
u32 PTSHigh; | |
}; | |
enum WaveFormatMPEGLayer3Flags : u32 { | |
MPEGLAYER3_FLAG_PADDING_ISO, | |
MPEGLAYER3_FLAG_PADDING_ON, | |
MPEGLAYER3_FLAG_PADDING_OFF | |
}; | |
struct WaveFormatMPEGLayer3 : WaveFormatEx { | |
u16 id; | |
WaveFormatMPEGLayer3Flags flags; | |
u16 blockSize; | |
u16 framesPerBlock; | |
u16 codecDelay; | |
}; | |
struct WaveFact { | |
u32 uncompressedSize; | |
}; | |
enum SampleLookupType : u32 { | |
TYPE_LOOP_FORWARD, | |
TYPE_LOOP_ALTERNATE, | |
TYPE_LOOP_BACKWARD, | |
}; | |
struct SampleLookup { | |
u32 id; | |
SampleLookupType type; | |
u32 start; | |
u32 end; | |
u32 fraction; | |
u32 playCount; | |
}; | |
struct WaveSample { | |
u32 manufacturer; | |
u32 product; | |
u32 samplePeriod; | |
u32 MIDIUnityNote; | |
u32 MIDIPitchFraction; | |
u32 SMPTEFormat; | |
u32 SMPTEOffset; | |
u32 numSampleLoops; | |
u32 sampleLoopsSize; | |
SampleLookup lookups[numSampleLoops]; | |
}; | |
struct WaveCuePoint { | |
u32 indentifier; | |
u32 position; | |
char chunkID[4]; | |
u32 chunkStart; | |
u32 blockStart; | |
u32 sampleOffset; | |
}; | |
struct WaveCue { | |
u32 numCuePoints; | |
WaveCuePoint cuePoints[numCuePoints]; | |
}; | |
struct WaveLabel { | |
u32 id; | |
char text[]; | |
padding[sizeof(text) % 2]; | |
}; | |
using WaveNote = WaveLabel; | |
struct WaveListItem : WaveChunk { | |
if (chunkId == "labl") { | |
WaveLabel label; | |
} else if (chunkId == "note") { | |
WaveNote note; | |
} else { | |
padding[(chunkSize + 1) >> 1 << 1]; | |
} | |
}; | |
u64 listEnd; | |
struct WaveList { | |
char type[4]; | |
WaveListItem item[while ($ < listEnd)]; | |
}; | |
u32 paddedChunkSize; | |
struct WavData { | |
WaveChunk chunk; | |
paddedChunkSize = (chunk.chunkSize + 1) >> 1 << 1; | |
if (chunk.chunkId == "fmt ") { | |
WaveFormat fmt; | |
if (fmt.formatTag == WaveFormatType::PCM) { | |
WaveFormatPCM pcmExtraData; | |
padding[paddedChunkSize - sizeof(fmt) - sizeof(pcmExtraData)]; | |
} else if (fmt.formatTag == WaveFormatType::MS_ADPCM) { | |
WaveFormatMSADPCM msAdpcmExtraData; | |
padding[paddedChunkSize - sizeof(fmt) - sizeof(msAdpcmExtraData)]; | |
} else if (fmt.formatTag == WaveFormatType::MPEG) { | |
WaveFormatMPEG mpegExtraData; | |
padding[paddedChunkSize - sizeof(fmt) - sizeof(mpegExtraData)]; | |
} else if (fmt.formatTag == WaveFormatType::MPEGLAYER3) { | |
WaveFormatMPEGLayer3 mpegLayer3ExtraData; | |
padding[paddedChunkSize - sizeof(fmt) - sizeof(mpegLayer3ExtraData)]; | |
} else if (fmt.formatTag == WaveFormatType::IEEEFloatingPoint) { | |
WaveFormatIEEEFloatingPoint ieeFloatingPointExtraData; | |
padding[paddedChunkSize - sizeof(fmt) - sizeof(ieeFloatingPointExtraData)]; | |
} else { | |
WaveFormatExDummy unknown; | |
padding[paddedChunkSize - sizeof(fmt) - sizeof(unknown)]; | |
} | |
} else if (chunk.chunkId == "data") { | |
padding[paddedChunkSize]; | |
} else if (chunk.chunkId == "fact") { | |
WaveFact fact; | |
padding[paddedChunkSize - sizeof(fact)]; | |
} else if (chunk.chunkId == "smpl") { | |
WaveSample smpl; | |
padding[paddedChunkSize - sizeof(smpl)]; | |
} else if (chunk.chunkId == "cue ") { | |
WaveCue cue; | |
padding[paddedChunkSize - sizeof(cue)]; | |
} else if (chunk.chunkId == "LIST") { | |
listEnd = $ + chunk.chunkSize; | |
WaveList list; | |
padding[paddedChunkSize % 1]; | |
} else { | |
padding[paddedChunkSize]; | |
} | |
}; | |
} | |
/// Wrapper for WAV | |
struct WAV<auto Size> { | |
le WAV::RiffHeader header; | |
le WAV::WavData data[while ($ < std::ptr::relative_to_parent(0) + Size)]; | |
}; | |
/// Copied from `elf.hexpat` | |
/// With slight modifications | |
namespace ELF { | |
using BitfieldOrder = std::core::BitfieldOrder; | |
using EI_ABIVERSION = u8; | |
using Elf32_Addr = u32; | |
using Elf32_BaseAddr = u32; | |
using Elf32_BaseOff = u32; | |
using Elf32_Half = u16; | |
using Elf32_Off = u32; | |
using Elf32_Sword = s32; | |
using Elf32_VAddr = u32; | |
using Elf32_Word = u32; | |
using Elf64_Addr = u64; | |
using Elf64_BaseAddr = u64; | |
using Elf64_BaseOff = u64; | |
using Elf64_Half = u16; | |
using Elf64_Off = u64; | |
using Elf64_Sword = s32; | |
using Elf64_Sxword = s64; | |
using Elf64_VAddr = u64; | |
using Elf64_Word = u32; | |
using Elf64_Xword = u64; | |
using E_VERSION = u32; | |
enum EI_CLASS : u8 { | |
ELFCLASSNONE = 0x00, | |
ELFCLASS32 = 0x01, | |
ELFCLASS64 = 0x02, | |
ELFCLASSNUM = 0x03, | |
}; | |
enum EI_DATA : u8 { | |
ELFDATANONE = 0x00, | |
ELFDATA2LSB = 0x01, | |
ELFDATA2MSB = 0x02, | |
ELFDATANUM = 0x03, | |
}; | |
enum EI_OSABI : u8 { | |
NONE = 0x00, | |
SYSV = 0x00, | |
HPUX = 0x01, | |
NetBSD = 0x02, | |
Linux = 0x03, | |
GNUHurd = 0x04, | |
Solaris = 0x06, | |
AIX = 0x07, | |
IRIX = 0x08, | |
FreeBSD = 0x09, | |
Tru64 = 0x0A, | |
NovellModesto = 0x0B, | |
OpenBSD = 0x0C, | |
OpenVMS = 0x0D, | |
NonStopKernel = 0x0E, | |
AROS = 0x0F, | |
FenixOS = 0x10, | |
CloudABI = 0x11, | |
OpenVOS = 0x12, | |
ARM_EABI = 0x40, | |
ARM = 0x61, | |
CELL_LV2 = 0x66, // CELL LV2 | |
STANDALONE = 0xFF, | |
}; | |
enum EI_VERSION : u8 { | |
NONE = 0x00, | |
CURRENT = 0x01, | |
}; | |
enum EM : u16 { | |
// Some of them have only numbers in the name, so, | |
// we're starting all of them with `_` | |
_NONE = 0x0000, | |
_M32 = 0x0001, | |
_SPARC = 0x0002, | |
_386 = 0x0003, | |
_68K = 0x0004, | |
_88K = 0x0005, | |
_IAMCU = 0x0006, | |
_860 = 0x0007, | |
_MIPS = 0x0008, | |
_S370 = 0x0009, | |
_MIPS_RS4_BE = 0x000a, | |
_PARISC = 0x000f, | |
_VPP500 = 0x0011, | |
_SPARC32PLUS = 0x0012, | |
_960 = 0x0013, | |
_PPC = 0x0014, | |
_PPC64 = 0x0015, | |
_S390 = 0x0016, | |
_SPU = 0x0017, | |
_V800 = 0x0024, | |
_FR20 = 0x0025, | |
_RH32 = 0x0026, | |
_RCE = 0x0027, | |
_ARM = 0x0028, | |
_ALPHA = 0x0029, | |
_SH = 0x002A, | |
_SPARCV9 = 0x002B, | |
_TRICORE = 0x002C, | |
_ARC = 0x002D, | |
_H8_300 = 0x002E, | |
_H8_300H = 0x002F, | |
_H8S = 0x0030, | |
_H8_500 = 0x0031, | |
_IA_64 = 0x0032, | |
_MIPS_X = 0x0033, | |
_COLDFIRE = 0x0034, | |
_68HC12 = 0x0035, | |
_MMA = 0x0036, | |
_PCP = 0x0037, | |
_NCPU = 0x0038, | |
_NDR1 = 0x0039, | |
_STARCORE = 0x003A, | |
_ME16 = 0x003B, | |
_ST100 = 0x003C, | |
_TINYJ = 0x003D, | |
_X86_64 = 0x003E, | |
_PDSP = 0x003F, | |
_PDP10 = 0x0040, | |
_PDP11 = 0x0041, | |
_FX66 = 0x0042, | |
_ST9PLUS = 0x0043, | |
_ST7 = 0x0044, | |
_68HC16 = 0x0045, | |
_68HC11 = 0x0046, | |
_68HC08 = 0x0047, | |
_68HC05 = 0x0048, | |
_SVX = 0x0049, | |
_ST19 = 0x004A, | |
_VAX = 0x004B, | |
_CRIS = 0x004C, | |
_JAVELIN = 0x004D, | |
_FIREPATH = 0x004E, | |
_ZSP = 0x004F, | |
_MMIX = 0x0050, | |
_HUANY = 0x0051, | |
_PRISM = 0x0052, | |
_AVR = 0x0053, | |
_FR30 = 0x0054, | |
_D10V = 0x0055, | |
_D30V = 0x0056, | |
_V850 = 0x0057, | |
_M32R = 0x0058, | |
_MN10300 = 0x0059, | |
_MN10200 = 0x005A, | |
_PJ = 0x005B, | |
_OPENRISC = 0x005C, | |
_ARC_COMPACT = 0x005D, | |
_XTENSA = 0x005E, | |
_VIDEOCORE = 0x005F, | |
_TMM_GPP = 0x0060, | |
_NS32K = 0x0061, | |
_TPC = 0x0062, | |
_SNP1K = 0x0063, | |
_ST200 = 0x0064, | |
_IP2K = 0x0065, | |
_MAX = 0x0066, | |
_CR = 0x0067, | |
_F2MC16 = 0x0068, | |
_MSP430 = 0x0069, | |
_BLACKFIN = 0x006A, | |
_SE_C33 = 0x006B, | |
_SEP = 0x006C, | |
_ARCA = 0x006D, | |
_UNICORE = 0x006E, | |
_EXCESS = 0x006F, | |
_DXP = 0x0070, | |
_ALTERA_NIOS2 = 0x0071, | |
_CRX = 0x0072, | |
_XGATE = 0x0073, | |
_C166 = 0x0074, | |
_M16C = 0x0075, | |
_DSPIC30F = 0x0076, | |
_CE = 0x0077, | |
_M32C = 0x0078, | |
_TSK3000 = 0x0083, | |
_RS08 = 0x0084, | |
_SHARC = 0x0085, | |
_ECOG2 = 0x0086, | |
_SCORE7 = 0x0087, | |
_DSP24 = 0x0088, | |
_VIDEOCORE3 = 0x0089, | |
_LATTICEMICO32 = 0x008A, | |
_SE_C17 = 0x008B, | |
_TI_C6000 = 0x008C, | |
_TI_C2000 = 0x008D, | |
_TI_C5500 = 0x008E, | |
_TI_ARP32 = 0x008F, | |
_TI_PRU = 0x0090, | |
_MMDSP_PLUS = 0x00A0, | |
_CYPRESS_M8C = 0x00A1, | |
_R32C = 0x00A2, | |
_TRIMEDIA = 0x00A3, | |
_QDSP6 = 0x00A4, | |
_8051 = 0x00A5, | |
_STXP7X = 0x00A6, | |
_NDS32 = 0x00A7, | |
_ECOG1 = 0x00A8, | |
_ECOG1X = 0x00A8, | |
_MAXQ30 = 0x00A9, | |
_XIMO16 = 0x00AA, | |
_MANIK = 0x00AB, | |
_CRAYNV2 = 0x00AC, | |
_RX = 0x00AD, | |
_METAG = 0x00AE, | |
_MCST_ELBRUS = 0x00AF, | |
_ECOG16 = 0x00B0, | |
_CR16 = 0x00B1, | |
_ETPU = 0x00B2, | |
_SLE9X = 0x00B3, | |
_L10M = 0x00B4, | |
_K10M = 0x00B5, | |
_AARCH64 = 0x00B7, | |
_AVR32 = 0x00B9, | |
_STM8 = 0x00BA, | |
_TILE64 = 0x00BB, | |
_TILEPRO = 0x00BC, | |
_MICROBLAZE = 0x00BD, | |
_CUDA = 0x00BE, | |
_TILEGX = 0x00BF, | |
_CLOUDSHIELD = 0x00C0, | |
_COREA_1ST = 0x00C1, | |
_COREA_2ND = 0x00C2, | |
_ARC_COMPACT2 = 0x00C3, | |
_OPEN8 = 0x00C4, | |
_RL78 = 0x00C5, | |
_VIDEOCORE5 = 0x00C6, | |
_78KOR = 0x00C7, | |
_56800EX = 0x00C8, | |
_BA1 = 0x00C9, | |
_BA2 = 0x00CA, | |
_XCORE = 0x00CB, | |
_MCHP_PIC = 0x00CC, | |
_INTEL205 = 0x00CD, | |
_INTEL206 = 0x00CE, | |
_INTEL207 = 0x00CF, | |
_INTEL208 = 0x00D0, | |
_INTEL209 = 0x00D1, | |
_KM32 = 0x00D2, | |
_KMX32 = 0x00D3, | |
_KMX16 = 0x00D4, | |
_KMX8 = 0x00D5, | |
_KVARC = 0x00D6, | |
_CDP = 0x00D7, | |
_COGE = 0x00D8, | |
_COOL = 0x00D9, | |
_NORC = 0x00DA, | |
_CSR_KALIMBA = 0x00DB, | |
_Z80 = 0x00DC, | |
_VISIUM = 0x00DD, | |
_FT32 = 0x00DE, | |
_MOXIE = 0x00DF, | |
_AMDGPU = 0x00E0, | |
_RISCV = 0x00F3, | |
_BPF = 0x00F7, | |
_CSKY = 0x00FC, | |
_LOONGARCH = 0x0102, | |
_NUM = 0x0103, | |
}; | |
enum ET : u16 { | |
NONE = 0x0000, | |
REL = 0x0001, | |
EXEC = 0x0002, | |
DYN = 0x0003, | |
CORE = 0x0004, | |
NUM = 0x0005, | |
/// OS-specific range | |
SCE_EXEC = 0xFE00, // SCE Executable - PRX2 | |
RANGE_OS = 0xFE01 ... 0xFE03, | |
SCE_RELEXEC = 0xFE04, // SCE Relocatable Executable - PRX2 | |
RANGE_OS = 0xFE05 ... 0xFE0B, | |
SCE_STUBLIB = 0xFE0C, // SCE SDK Stubs | |
RANGE_OS = 0xFE0D ... 0xFE0F, | |
SCE_DYNEXEC = 0xFE10, // SCE EXEC_ASLR (PS4 Executable with ASLR) | |
RANGE_OS = 0xFE11 ... 0xFE17, | |
SCE_DYNAMIC = 0xFE18, // ? | |
RANGE_OS = 0xFE19 ... 0xFEFF, | |
/// Processor-specific range | |
RANGE_PROC = 0xFF00 ... 0xFF7F, | |
SCE_IOPRELEXEC = 0xFF80, // SCE IOP Relocatable Executable | |
SCE_IOPRELEXEC2 = 0xFF81, // SCE IOP Relocatable Executable Version 2 | |
RANGE_PROC = 0xFF82 ... 0xFF8F, | |
SCE_EERELEXEC = 0xFF90, // SCE EE Relocatable Executable | |
SCE_EERELEXEC2 = 0xFF91, // SCE EE Relocatable Executable Version 2 | |
RANGE_PROC = 0xFF92 ... 0xFF9F, | |
SCE_PSPRELEXEC = 0xFFA0, // SCE PSP Relocatable Executable | |
RANGE_PROC = 0xFFA1 ... 0xFFA3, | |
SCE_PPURELEXEC = 0xFFA4, // SCE PPU Relocatable Executable | |
SCE_ARMRELEXEC = 0xFFA5, // SCE ARM Relocatable Executable (PS Vita System Software earlier or equal 0.931.010)? | |
RANGE_PROC = 0xFFA6 ... 0xFFF7, | |
SCE_PSPOVERLAY = 0xFFA8, // PSP Overlay file | |
RANGE_PROC = 0xFFA9 ... 0xFFFF, | |
}; | |
enum DT : u32 { | |
NULL = 0x0, | |
NEEDED = 0x1, | |
PLTRELSZ = 0x2, | |
PLTGOT = 0x3, | |
HASH = 0x4, | |
STRTAB = 0x5, | |
SYMTAB = 0x6, | |
RELA = 0x7, | |
RELASZ = 0x8, | |
RELAENT = 0x9, | |
STRSZ = 0xA, | |
SYMENT = 0xB, | |
INIT = 0xC, | |
FINI = 0xD, | |
SONAME = 0xE, | |
RPATH = 0xF, | |
SYMBOLIC = 0x10, | |
REL = 0x11, | |
RELSZ = 0x12, | |
RELENT = 0x13, | |
PLTREL = 0x14, | |
DEBUG = 0x15, | |
TEXTREL = 0x16, | |
JMPREL = 0x17, | |
BIND_NOW = 0x18, | |
INIT_ARRAY = 0x19, | |
FINI_ARRAY = 0x1A, | |
INIT_ARRAYSZ = 0x1B, | |
FINI_ARRAYSZ = 0x1C, | |
RUNPATH = 0x1D, | |
FLAGS = 0x1E, | |
PREINIT_ARRAY = 0x20, | |
PREINIT_ARRAYSZ = 0x21, | |
MAXPOSTAGS = 0x22, | |
NUM = 0x23, | |
/// OS-specific range | |
SUNW_AUXILIARY = 0x6000000D, | |
SUNW_RTLDINF = 0x6000000E, | |
SUNW_FILTER = 0x6000000F, | |
SUNW_CAP = 0x60000010, | |
SUNW_SYMTAB = 0x60000011, | |
SUNW_SYMSZ = 0x60000012, | |
SUNW_SORTENT = 0x60000013, | |
SUNW_SYMSORT = 0x60000014, | |
SUNW_SYMSORTSZ = 0x60000015, | |
SUNW_TLSSORT = 0x60000016, | |
SUNW_TLSSORTSZ = 0x60000017, | |
SUNW_STRPAD = 0x60000019, | |
SUNW_LDMACH = 0x6000001B, | |
RANGE_OS = 0x6000001C ... 0x6FFFF000, | |
/// Processor-specific range | |
RANGE_PROC = 0x70000000 ... 0x7FFFFFFC, | |
AUXILIARY = 0x7FFFFFFD, | |
USED = 0x7FFFFFFE, | |
FILTER = 0x7FFFFFFF, | |
/// Use the Dyn.d_un.d_val field of the Elf*_Dyn structure | |
RANGE_VALRNG = 0x6FFFFD00 ... 0x6FFFFDF4, | |
GNU_PRELINKED = 0x6FFFFDF5, | |
GNU_CONFLICTSZ = 0x6FFFFDF6, | |
GNU_LIBLISTSZ = 0x6FFFFDF7, | |
CHECKSUM = 0x6FFFFDF8, | |
PLTPADSZ = 0x6FFFFDF9, | |
MOVEENT = 0x6FFFFDFA, | |
MOVESZ = 0x6FFFFDFB, | |
FEATURE_1 = 0x6FFFFDFC, | |
POSFLAG_1 = 0x6FFFFDFD, | |
SYMINSZ = 0x6FFFFDFE, | |
SYMINENT = 0x6FFFFDFF, | |
/// Use the Dyn.d_un.d_ptr field of the Elf*_Dyn structure. | |
RANGE_ADDRRNG = 0x6FFFFE00 ... 0x6FFFFEF4, | |
GNU_HASH = 0x6FFFFEF5, | |
TLSDESC_PLT = 0x6FFFFEF6, | |
TLSDESC_GOT = 0x6FFFFEF7, | |
GNU_CONFLICT = 0x6FFFFEF8, | |
GNU_LIBLIST = 0x6FFFFEF9, | |
CONFIG = 0x6FFFFEFA, | |
DEPAUDIT = 0x6FFFFEFB, | |
AUDIT = 0x6FFFFEFC, | |
PLTPAD = 0x6FFFFEFD, | |
MOVETAB = 0x6FFFFEFE, | |
SYMINFO = 0x6FFFFEFF, | |
/// GNU extensions | |
VERSYM = 0x6FFFFFF0, | |
RELACOUNT = 0x6FFFFFF9, | |
RELCOUNT = 0x6FFFFFFA, | |
FLAGS_1 = 0x6FFFFFFB, | |
VERDEF = 0x6FFFFFFC, | |
VERDEFNUM = 0x6FFFFFFD, | |
VERNEED = 0x6FFFFFFE, | |
VERNEEDNUM = 0x6FFFFFFF, | |
}; | |
enum PT : Elf32_Word { | |
NULL = 0x00, | |
LOAD = 0x01, | |
DYNAMIC = 0x02, | |
INTERP = 0x03, | |
NOTE = 0x04, | |
SHLIB = 0x05, | |
PHDR = 0x06, | |
TLS = 0x07, | |
NUM = 0x08, | |
/// OS-specific range | |
SCE_RELA = 0x60000000, | |
SCE_LICINFO_1 = 0x60000001, | |
SCE_LICINFO_2 = 0x60000002, | |
RANGE_OS = 0x60000003 ... 0x60FFFFFF, | |
SCE_DYNLIBDATA = 0x61000000, | |
SCE_PROCESS_PARAM = 0x61000001, | |
SCE_MODULE_PARAM = 0x61000002, | |
RANGE_OS = 0x61000003 ... 0x6100000F, | |
SCE_RELRO = 0x61000010, // for PS4 | |
RANGE_OS = 0x61000011 ... 0x6474E54F, | |
GNU_EH_FRAME = 0x6474E550, | |
GNU_STACK = 0x6474E551, | |
GNU_RELRO = 0x6474E552, | |
GNU_PROPERTY = 0x6474E553, | |
RANGE_OS = 0x6474E554 ... 0x6FFFFEFF, | |
SCE_COMMENT = 0x6FFFFF00, | |
SCE_LIBVERSION = 0x6FFFFF01, | |
RANGE_OS = 0x6FFFFF02 ... 0x6FFFFFF9, | |
SUNWBSS = 0x6FFFFFFA, | |
SUNWSTACK = 0x6FFFFFFB, | |
RANGE_OS = 0x6FFFFFFC ... 0x6FFFFFFF, | |
/// Processor-specific range | |
ARM_ARCHEXT = 0x70000000, | |
ARM_UNWIND = 0x70000001, | |
RANGE_PROC = 0x70000002 ... 0x7000007F, | |
SCE_IOPMOD = 0x70000080, | |
RANGE_PROC = 0x70000081 ... 0x7000008F, | |
SCE_EEMOD = 0x70000090, | |
RANGE_PROC = 0x70000091 ... 0x7000009F, | |
SCE_PSPRELA = 0x700000A0, // PSP Relocation data segment | |
SCE_PSPRELA2 = 0x700000A1, // PSP Relocation data segment Version 2 | |
RANGE_PROC = 0x700000A2 ... 0x700000A3, | |
SCE_PPURELA = 0x700000A4, | |
RANGE_PROC = 0x700000A5 ... 0x700000A7, | |
SCE_SEGSYM = 0x700000A8, | |
RANGE_PROC = 0x700000A9 ... 0x7FFFFFFF, | |
}; | |
enum ELFCOMPRESS : u8 { | |
ZLIB = 0x01, | |
ZSTD = 0x02, | |
/// OS-specific range | |
RANGE_OS = 0x60000000 ... 0x6fffffff, | |
/// Processor-specific range | |
RANGE_PROC = 0x70000000 ... 0x7fffffff, | |
}; | |
enum SHN : u16 { | |
UNDEF = 0x00, | |
BEFORE = 0xFF00, | |
AFTER = 0xFF01, | |
/// Processor-specific range | |
RANGE_PROC = 0xFF02 ... 0xFF1F, | |
/// OS-specific range | |
RANGE_OS = 0xFF20 ... 0xFF3F, | |
ABS = 0xFFF1, | |
COMMON = 0xFFF2, | |
XINDEX = 0xFFFF, | |
}; | |
enum SHT : Elf32_Word { | |
NULL = 0x00, | |
PROGBITS = 0x01, | |
SYMTAB = 0x02, | |
STRTAB = 0x03, | |
RELA = 0x04, | |
HASH = 0x05, | |
DYNAMIC = 0x06, | |
NOTE = 0x07, | |
NOBITS = 0x08, | |
REL = 0x09, | |
SHLIB = 0x0A, | |
DYNSYM = 0x0B, | |
UNKNOWN12 = 0x0C, | |
UNKNOWN13 = 0x0D, | |
INIT_ARRAY = 0x0E, | |
FINI_ARRAY = 0x0F, | |
PREINIT_ARRAY = 0x10, | |
GROUP = 0x11, | |
SYMTAB_SHNDX = 0x12, | |
RELR = 0x13, | |
NUM = 0x14, | |
/// OS-specific range | |
SCE_RELA = 0x60000000, | |
SCE_NID = 0x61000001, | |
RANGE_OS = 0x60000002 ... 0x6FFF46FF, | |
GNU_INCREMENTAL_INPUTS = 0x6FFF4700, | |
RANGE_OS = 0x6FFF4701 ... 0x6FFFFFF4, | |
GNU_ATTRIBUTES = 0x6FFFFFF5, | |
GNU_HASH = 0x6FFFFFF6, | |
GNU_LIBLIST = 0x6FFFFFF7, | |
CHECKSUM = 0x6FFFFFF8, | |
RANGE_OS = 0x6FFFFFF9, | |
SUNW_MOVE = 0x6FFFFFFA, | |
SUNW_COMDAT = 0x6FFFFFFB, | |
SUNW_syminfo = 0x6FFFFFFC, | |
GNU_verdef = 0x6FFFFFFD, | |
GNU_verneed = 0x6FFFFFFE, | |
GNU_versym = 0x6FFFFFFF, | |
/// Processor-specific range | |
RANGE_PROC = 0x70000000, | |
ARM_EXIDX = 0x70000001, | |
ARM_PREEMPTMAP = 0x70000002, | |
ARM_ATTRIBUTES = 0x70000003, | |
ARM_DEBUGOVERLAY = 0x70000004, | |
ARM_OVERLAYSECTION = 0x70000005, | |
RANGE_PROC = 0x70000006 ... 0x7000007F, | |
SCE_IOPMOD = 0x70000080, | |
RANGE_PROC = 0x70000081 ... 0x7000008F, | |
SCE_EEMOD = 0x70000090, | |
RANGE_PROC = 0x70000091 ... 0x7000009F, | |
SCE_PSPRELA = 0x700000A0, | |
RANGE_PROC = 0x700000A1 ... 0x700000A3, | |
SCE_PPURELA = 0x700000A4, | |
RANGE_PROC = 0x700000A5 ... 0x7FFFFFFF, | |
/// Application-specific range | |
RANGE_USER = 0x80000000 ... 0x8FFFFFFF, | |
}; | |
enum STV : u8 { | |
DEFAULT = 0x00, | |
INTERNAL = 0x01, | |
HIDDEN = 0x02, | |
PROTECTED = 0x03, | |
}; | |
enum SYMINFO_BT : Elf32_Half { | |
SELF = 0xFFFF, | |
PARENT = 0xFFFE, | |
NONE = 0xFFFD, | |
}; | |
enum VER_DEF : Elf32_Half { | |
NON = 0x00, | |
CURRENT = 0x01, | |
NUM = 0x02, | |
}; | |
enum VER_NDX : Elf32_Half { | |
LOCAL = 0x00, | |
GLOBAL = 0x01, | |
ELIMINATE = 0xFF01, | |
}; | |
enum VER_NEED : Elf32_Half { | |
NONE = 0x00, | |
CURRENT = 0x01, | |
NUM = 0x02, | |
}; | |
bitfield SYMINFO_FLG { | |
DIRECT : 1; | |
PASSTHRU : 1; | |
COPY : 1; | |
LAZYLOAD : 1; | |
DIRECTBIND : 1; | |
NOEXTDIRECT : 1; | |
padding : 10; | |
}; | |
bitfield ST { | |
ST_BIND : 4; | |
ST_TYPE : 4; | |
} [[bitfield_order(BitfieldOrder::MostToLeastSignificant, 8)]]; | |
bitfield SHF { | |
WRITE : 1; | |
ALLOC : 1; | |
EXECINSTR : 1; | |
padding : 1; | |
MERGE : 1; | |
STRINGS : 1; | |
INFO_LINK : 1; | |
LINK_ORDER : 1; | |
OS_NONCONFORMING : 1; | |
GROUP : 1; | |
TLS : 1; | |
COMPRESSED : 1; | |
UNKNOWN : 8; | |
MASKOS : 8; | |
MASKPROC : 4; | |
}; | |
bitfield ELF32_R_INFO { | |
SYM : 8; | |
TYPE : 8; | |
} [[bitfield_order(BitfieldOrder::MostToLeastSignificant, 16)]]; | |
bitfield ELF64_R_INFO { | |
SYM : 32; | |
TYPE : 32; | |
} [[bitfield_order(BitfieldOrder::MostToLeastSignificant, 64)]]; | |
bitfield PF { | |
X : 1; | |
W : 1; | |
R : 1; | |
padding : 17; | |
SPU_X : 1; | |
SPU_W : 1; | |
SPU_R : 1; | |
padding : 1; | |
RSX_X : 1; | |
RSX_W : 1; | |
RSX_R : 1; | |
padding : 1; | |
MASKPROC : 4; | |
}; | |
struct E_IDENT { | |
type::Magic<"\x7fELF"> EI_MAG; | |
EI_CLASS EI_CLASS; | |
EI_DATA EI_DATA; | |
EI_VERSION EI_VERSION; | |
EI_OSABI EI_OSABI; | |
EI_ABIVERSION EI_ABIVERSION; | |
padding[7]; | |
}; | |
struct Elf32_Ehdr { | |
ET e_type; | |
EM e_machine; | |
E_VERSION e_version; | |
Elf32_VAddr e_entry; | |
Elf32_Off e_phoff; | |
Elf32_Off e_shoff; | |
Elf32_Word e_flags; | |
Elf32_Half e_ehsize; | |
Elf32_Half e_phentsize; | |
Elf32_Half e_phnum; | |
Elf32_Half e_shentsize; | |
Elf32_Half e_shnum; | |
Elf32_Half e_shstrndx; | |
}; | |
struct Elf64_Ehdr { | |
ET e_type; | |
EM e_machine; | |
E_VERSION e_version; | |
Elf64_VAddr e_entry; | |
Elf64_Off e_phoff; | |
Elf64_Off e_shoff; | |
Elf64_Word e_flags; | |
Elf64_Half e_ehsize; | |
Elf64_Half e_phentsize; | |
Elf64_Half e_phnum; | |
Elf64_Half e_shentsize; | |
Elf64_Half e_shnum; | |
Elf64_Half e_shstrndx; | |
}; | |
struct Elf32_Phdr<auto Origin> { | |
PT p_type; | |
Elf32_Off p_offset; | |
Elf32_Addr p_vaddr; | |
Elf32_Addr p_paddr; | |
Elf32_Word p_filesz; | |
Elf32_Word p_memsz; | |
PF p_flags; | |
Elf32_Word p_align; | |
if (p_offset >= 0 && p_filesz > 0 && (Origin + p_offset + p_filesz) <= std::mem::size() && (Origin + p_filesz) <= std::mem::size()) | |
u8 p_data[p_filesz] @ Origin + p_offset [[sealed]]; | |
}; | |
struct Elf64_Phdr<auto Origin> { | |
PT p_type; | |
PF p_flags; | |
Elf64_Off p_offset; | |
Elf64_Addr p_vaddr; | |
Elf64_Addr p_paddr; | |
Elf64_Xword p_filesz; | |
Elf64_Xword p_memsz; | |
Elf64_Xword p_align; | |
if (p_offset >= 0 && p_filesz > 0 && (Origin + p_offset + p_filesz) <= std::mem::size() && (Origin + p_filesz) <= std::mem::size()) | |
u8 p_data[p_filesz] @ Origin + p_offset [[sealed]]; | |
}; | |
struct Elf32_Chdr { | |
u32 ch_type; | |
Elf32_Word ch_size; | |
Elf32_Word ch_addralign; | |
}; | |
struct Elf32_Rel { | |
Elf32_Off r_offset; | |
ELF32_R_INFO r_info; | |
}; | |
struct Elf32_Rela { | |
Elf32_Off r_offset; | |
ELF32_R_INFO r_info; | |
Elf32_Sword r_addend; | |
}; | |
struct Elf32_Sym { | |
u32 st_name; | |
Elf32_VAddr st_value; | |
Elf32_Word st_size; | |
ST st_info; | |
STV st_other; | |
u16 st_shndx; | |
}; | |
struct Elf32_Syminfo { | |
u16 si_boundto; | |
SYMINFO_FLG si_flags; | |
}; | |
s64 stringTableIndex; | |
struct String { // Basically `std::string::NullString`, but that one works with `std::core::set_display_name` | |
char value[while(std::mem::read_unsigned($, sizeof(char)) != 0x00)]; | |
char tail; // NULL terminator | |
} [[sealed, format("ELF::format_string")]]; | |
fn format_string(String string) { | |
return string.value; | |
}; | |
struct Elf32_Shdr<auto Origin> { | |
u32 sh_name; | |
SHT sh_type; | |
SHF sh_flags; | |
u32 sh_addr; | |
u32 sh_offset; | |
Elf32_Word sh_size; | |
Elf32_Word sh_link; | |
Elf32_Word sh_info; | |
Elf32_Word sh_addralign; | |
Elf32_Word sh_entsize; | |
if (sh_size > 0 && (Origin + sh_offset + sh_size) <= std::mem::size()) { | |
if (sh_type == SHT::NOBITS || sh_type == SHT::NULL) { | |
// Section has no data | |
} else if (sh_type == SHT::STRTAB) { | |
String stringTable[while($ < (Origin + sh_offset + sh_size))] @ Origin + sh_offset; | |
} else if (sh_type == SHT::SYMTAB || sh_type == SHT::DYNSYM) { | |
Elf32_Sym symbolTable[sh_size / sh_entsize] @ Origin + sh_offset; | |
} else if (sh_type == SHT::INIT_ARRAY || sh_type == SHT::FINI_ARRAY) { | |
u32 pointer[while($ < (Origin + sh_offset + sh_size))] @ Origin + sh_offset; | |
} else { | |
u8 data[sh_size] @ Origin + sh_offset [[sealed]]; | |
} | |
} | |
}; | |
struct Elf64_Chdr { | |
u32 ch_type; | |
Elf64_Word ch_size; | |
Elf64_Word ch_addralign; | |
}; | |
struct Elf64_Rel { | |
Elf64_Off r_offset; | |
ELF64_R_INFO r_info; | |
}; | |
struct Elf64_Rela { | |
Elf64_Off r_offset; | |
ELF64_R_INFO r_info; | |
Elf64_Sxword r_addend; | |
}; | |
struct Elf64_Sym { | |
u32 st_name; | |
ST st_info; | |
STV st_other; | |
u16 st_shndx; | |
Elf64_VAddr st_value; | |
Elf64_Xword st_size; | |
}; | |
struct Elf64_Syminfo { | |
u16 si_boundto; | |
SYMINFO_FLG si_flags; | |
}; | |
struct Elf64_Shdr<auto Origin> { | |
u32 sh_name; | |
SHT sh_type; | |
SHF sh_flags; | |
padding[4]; | |
u64 sh_addr; | |
u64 sh_offset; | |
Elf64_Xword sh_size; | |
Elf64_Word sh_link; | |
Elf64_Word sh_info; | |
Elf64_Xword sh_addralign; | |
Elf64_Xword sh_entsize; | |
if (sh_size > 0 && (Origin + sh_offset + sh_size) <= std::mem::size()) { | |
if (sh_type == SHT::NOBITS || sh_type == SHT::NULL) { | |
// Section has no data | |
} else if (sh_type == SHT::STRTAB) { | |
String stringTable[while($ < (Origin + sh_offset + sh_size))] @ Origin + sh_offset; | |
} else if (sh_type == SHT::SYMTAB || sh_type == SHT::DYNSYM) { | |
Elf64_Sym symbolTable[sh_size / sh_entsize] @ Origin + sh_offset; | |
} else if (sh_type == SHT::INIT_ARRAY || sh_type == SHT::FINI_ARRAY) { | |
u32 pointer[while($ < (Origin + sh_offset + sh_size))] @ Origin + sh_offset; | |
} else { | |
u8 data[sh_size] @ Origin + sh_offset [[sealed]]; | |
} | |
} | |
}; | |
fn format_section_header(ref auto elf, ref auto shdr) { | |
u32 i = 0; | |
u32 nameAddress = addressof(elf.shdr[stringTableIndex].stringTable) + shdr.sh_name; | |
String string @ nameAddress; | |
if (sizeof(string.value) == 0) { | |
// Section name is empty | |
return ".<null>"; | |
} | |
return string; | |
}; | |
struct ELF<auto Origin> { | |
E_IDENT e_ident; | |
if (e_ident.EI_DATA == EI_DATA::ELFDATA2LSB) { | |
if (e_ident.EI_CLASS == EI_CLASS::ELFCLASS32) { | |
le Elf32_Ehdr ehdr; | |
stringTableIndex = ehdr.e_shstrndx; | |
le Elf32_Phdr<Origin> phdr[ehdr.e_phnum] @ Origin + ehdr.e_phoff; | |
le Elf32_Shdr<Origin> shdr[ehdr.e_shnum] @ Origin + ehdr.e_shoff; | |
} else if (e_ident.EI_CLASS == EI_CLASS::ELFCLASS64) { | |
le Elf64_Ehdr ehdr; | |
stringTableIndex = ehdr.e_shstrndx; | |
le Elf64_Phdr<Origin> phdr[ehdr.e_phnum] @ Origin + ehdr.e_phoff; | |
le Elf64_Shdr<Origin> shdr[ehdr.e_shnum] @ Origin + ehdr.e_shoff; | |
} | |
} | |
else { | |
if (e_ident.EI_CLASS == EI_CLASS::ELFCLASS32) { | |
be Elf32_Ehdr ehdr; | |
stringTableIndex = ehdr.e_shstrndx; | |
be Elf32_Phdr<Origin> phdr[ehdr.e_phnum] @ Origin + ehdr.e_phoff; | |
be Elf32_Shdr<Origin> shdr[ehdr.e_shnum] @ Origin + ehdr.e_shoff; | |
} else if (e_ident.EI_CLASS == EI_CLASS::ELFCLASS64) { | |
be Elf64_Ehdr ehdr; | |
stringTableIndex = ehdr.e_shstrndx; | |
be Elf64_Phdr<Origin> phdr[ehdr.e_phnum] @ Origin + ehdr.e_phoff; | |
be Elf64_Shdr<Origin> shdr[ehdr.e_shnum] @ Origin + ehdr.e_shoff; | |
} | |
} | |
}; | |
fn gen_shdr_disp_name(ref auto elf, ref auto pattern, str member_name, u8 member_index, u8 member_total) { | |
return std::format( | |
"[{:0{}}]{}", | |
member_index, | |
u8(std::math::log10(member_total)+1), | |
ELF::format_section_header(elf, pattern) | |
); | |
}; | |
fn displaySectionNames(ref auto elf) { | |
auto shdr_count = std::core::member_count(elf.shdr); | |
for (u32 i = 0, i < shdr_count, i += 1) { | |
std::core::set_display_name( | |
elf.shdr[i], | |
ELF::gen_shdr_disp_name(elf, elf.shdr[i], "shdr", i, shdr_count) | |
); | |
} | |
}; | |
} | |
/// Wrapper for ELF | |
struct ELF<auto Size> { | |
ELF::ELF<$> elf [[inline]]; | |
ELF::displaySectionNames(elf); | |
padding[Size - sizeof(this)]; // Artificially bloat size of struct to real size | |
}; | |
/// PlayStation System File | |
namespace PSF { | |
enum ValueType : u16 { | |
RSV_M = 0x0000, | |
FILE_M = 0x0100, | |
VALUE_STR8 = 0x0204, | |
VALUE_STR16 = 0x0304, | |
VALUE_INT = 0x0404, | |
VALUE_FLOAT = 0x0504, | |
VALUE_FILE4 = 0x0104, | |
VALUE_FILE8 = 0x0108, | |
VALUE_FILE16 = 0x0110, | |
VALUE_FILE32 = 0x0120, | |
VALUE_RSV4 = 0x0004, | |
VALUE_RSV8 = 0x0008, | |
VALUE_RSV16 = 0x0010, | |
VALUE_RSV32 = 0x0020, | |
}; | |
bitfield Region { | |
// Assumption based on PS3 and uOFW | |
bool JP : 1 [[comment("Japan")]]; | |
bool US : 1 [[comment("US, Canada (North America)")]]; | |
bool EU : 1 [[comment("Europe / Middle East / Africa")]]; | |
bool KR : 1 [[comment("Korea (South Korea)")]]; | |
bool UK : 1 [[comment("U.K. / Ireland")]]; | |
bool MX : 1 [[comment("Mexico, Central America, South America")]]; | |
bool AU : 1 [[comment("Australia / New Zealand (Oceania)")]]; | |
bool SA : 1 [[comment("Singapore / Malaysia (Southeast Asia)")]]; | |
bool TW : 1 [[comment("Taiwan")]]; | |
bool RU : 1 [[comment("Russia, Ukraine, India, Central Asia")]]; | |
bool CN : 1 [[comment("China")]]; | |
bool HK : 1 [[comment("Hong Kong")]]; | |
padding : 3; | |
bool ALL : 1 [[comment("All regions")]]; | |
padding : 16; | |
}; | |
fn indexKeyDesc(ref str key) { | |
match (key) { | |
("APP_VER" ): return "Application version"; | |
("ATTRIBUTE" ): return "Attribute"; | |
("BOOTABLE" ): return "Is bootable"; | |
("CATEGORY" ): return "Category"; | |
("DISC_ID" ): return "Disc ID"; | |
("DISC_NUMBER" ): return "Disc number"; | |
("DISC_TOTAL" ): return "Number of discs"; | |
("DISC_VERSION" ): return "Disc version"; | |
("DRIVER_PATH" ): return "Path to the device drivers"; | |
("GAMEDATA_ID" ): return "Game data ID"; | |
("HRKGMP_VER" ): return "HRKGMP version"; // Nobody really knows what this is | |
("LICENSE" ): return "License information"; | |
("MEMSIZE" ): return "Add extra RAM for application"; // Does this exists outside of Homebrew? | |
("PARENTAL_LEVEL" ): return "Parental-lock level"; | |
("PBOOT_TITLE" ): return "Patch title name"; | |
("PSP_SYSTEM_VER" ): return "PSP system version"; | |
("REGION" ): return "Region"; | |
("SHARING_ID" ): return "Sharing ID"; | |
("TARGET_APP_VER" ): return "Target application version"; | |
("TITLE" ): return "Title name (Default)"; | |
("TITLE_0" | "TITLE_00"): return "Title name (JA)"; | |
("TITLE_1" | "TITLE_01"): return "Title name (EN)"; | |
("TITLE_2" | "TITLE_02"): return "Title name (FR)"; | |
("TITLE_3" | "TITLE_03"): return "Title name (ES)"; | |
("TITLE_4" | "TITLE_04"): return "Title name (DE)"; | |
("TITLE_5" | "TITLE_05"): return "Title name (IT)"; | |
("TITLE_6" | "TITLE_06"): return "Title name (NL)"; | |
("TITLE_7" | "TITLE_07"): return "Title name (PT)"; | |
("TITLE_8" | "TITLE_08"): return "Title name (RU)"; | |
("TITLE_9" | "TITLE_09"): return "Title name (KO)"; | |
("TITLE_10" ): return "Title name (CH)"; | |
("TITLE_11" ): return "Title name (ZH)"; | |
("UPDATER_VER" ): return "Updater version"; | |
("USE_USB" ): return "Use USB peripheral"; | |
// TODO: Add more keys | |
(_): return null; | |
} | |
}; | |
struct Index { | |
type::Hex<u16> key_offset [[hidden]]; | |
ValueType value_type [[name("type")]]; | |
u32 value_used_size [[hidden]]; | |
u32 value_max_size [[hidden]]; | |
type::Hex<u32> value_offset [[hidden]]; | |
/// Pointers to the start of the key and value | |
auto key_ptr = parent.origin + parent.key_table_offset + key_offset; | |
auto val_ptr = parent.origin + parent.val_table_offset + value_offset; | |
std::string::NullString key @ key_ptr; | |
str name = PSF::indexKeyDesc(key) [[export]]; | |
match (value_type) { | |
(ValueType::VALUE_STR8): { | |
std::string::NullString value_raw @ val_ptr [[hidden]]; | |
match (key) { | |
// TODO: Handle all these values | |
("CATEGORY"): | |
match (value_raw) { | |
("MA"): str value = "MemoryStick App" [[export]]; | |
("ME"): str value = "PS1 Classic" [[export]]; // IDK why it's ME | |
("MG"): str value = "MemoryStick Game" [[export]]; | |
("MS"): str value = "MemoryStick Save" [[export]]; | |
("PG"): str value = "Game Patch" [[export]]; | |
("UG"): str value = "UMD Game" [[export]]; | |
("WG"): str value = "WLAN Game" [[export]]; | |
("EG"): std::string::NullString value @ addressof(value_raw); | |
("MM"): std::string::NullString value @ addressof(value_raw); | |
(_): std::string::NullString value @ addressof(value_raw); // Return as is | |
} | |
("DRIVER_PATH"): std::string::NullString value @ addressof(value_raw); | |
("GAMEDATA_ID"): std::string::NullString value @ addressof(value_raw); | |
(_): std::string::NullString value @ addressof(value_raw); // Return as is | |
} | |
} | |
(ValueType::VALUE_STR16): | |
std::string::NullString16 value @ val_ptr; | |
(ValueType::VALUE_INT): { | |
u32 value_raw @ val_ptr [[hidden]]; | |
match (key) { | |
// TODO: Handle all these values | |
("ATTRIBUTE" ): u32 value @ addressof(value_raw); | |
("BOOTABLE" ): u32 value @ addressof(value_raw); | |
("HRKGMP_VER"): u32 value @ addressof(value_raw); | |
("REGION" ): Region value @ addressof(value_raw); | |
("SHARING_ID"): u32 value @ addressof(value_raw); | |
(_): u32 value @ addressof(value_raw); // Return as is | |
} | |
} | |
(ValueType::VALUE_FLOAT): | |
float value @ val_ptr; | |
(ValueType::VALUE_FILE4): | |
str value = "FileRef(align=4)" [[export]]; | |
(ValueType::VALUE_FILE8): | |
str value = "FileRef(align=8)" [[export]]; | |
(ValueType::VALUE_FILE16): | |
str value = "FileRef(align=16)" [[export]]; | |
(ValueType::VALUE_FILE32): | |
str value = "FileRef(align=32)" [[export]]; | |
(ValueType::VALUE_RSV4): | |
str value = "Reserve(align=4)" [[export]]; | |
(ValueType::VALUE_RSV8): | |
str value = "Reserve(align=8)" [[export]]; | |
(ValueType::VALUE_RSV16): | |
str value = "Reserve(align=16)" [[export]]; | |
(ValueType::VALUE_RSV32): | |
str value = "Reserve(align=32)" [[export]]; | |
(_): | |
std::assert(false, "Unknown type"); | |
} | |
}; | |
struct Header<auto Size> { | |
/// Size of the header must be at least 0x14 | |
/// 0x4 - magic | |
/// 0x4 - version | |
/// 0x4 - key_table_offset | |
/// 0x4 - val_table_offset | |
/// 0x4 - tables_entries | |
if (Size < 0x14) | |
std::assert(false, "PSF Header size cannot be 0"); | |
/// Needed for relative to beginning of the header offsets | |
auto origin = $; | |
type::Magic<"\x00PSF"> magic; | |
type::BCD<4> version; | |
type::Hex<u32> key_table_offset [[hidden]]; | |
type::Hex<u32> val_table_offset [[hidden]]; | |
type::Hex<u32> tables_entries [[hidden]]; | |
/// As we are using calculated pointers inside, the size is undetermined | |
Index index[tables_entries]; | |
/// Adjust $ to compensate for the dynamic size of the index table | |
$ = origin + Size; | |
}; | |
fn displaySectionNames(ref auto psf) { | |
auto index_count = std::core::member_count(psf.index); | |
for (u32 i = 0, i < index_count, i += 1) { | |
std::core::set_display_name( | |
psf.index[i], | |
std::format( | |
"[{:0{}}]{}", | |
i, | |
u8(std::math::log10(index_count)+1), | |
psf.index[i].key | |
) | |
); | |
} | |
}; | |
} | |
/// Wrapper for PSF | |
struct PSF<auto Size> { | |
le PSF::Header<Size> header [[inline]]; | |
PSF::displaySectionNames(header); | |
}; | |
/// PlayStation Movie Format | |
namespace PSMF { | |
enum Version : u32 { | |
NUMBER_0012 = 0x30303132, // 0012 | |
NUMBER_0013 = 0x30303133, // 0013 | |
NUMBER_0014 = 0x30303134, // 0014 | |
NUMBER_0015 = 0x30303135, // 0015 | |
}; | |
struct EpEntry { | |
u48 pts [[comment("48-bit presentation time stamp")]]; | |
u32 rpn [[comment("Relative Position Number")]]; | |
}; | |
struct StreamEpMap<auto Count> { | |
EpEntry ep_entries[Count]; | |
}; | |
struct Video { | |
u8 width_div_16 [[comment("Width divided by 16")]]; | |
u8 height_div_16 [[comment("Height divided by 16")]]; | |
u16 reserved [[comment("Always 0")]]; | |
}; | |
struct Audio { | |
u16 reserved [[comment("Always 0")]]; | |
u8 channel_config [[comment("Channel configuration")]]; | |
u8 sampling_frequency [[comment("Sampling frequency")]]; | |
}; | |
struct StreamInfo<auto Origin> { | |
u8 stream_id [[comment("Stream ID")]]; | |
u8 private_stream_id [[comment("Private stream ID")]]; | |
u16 pstd_buffer [[comment("P-STD buffer size (Program Stream - System Target Decoder)")]]; | |
u32 ep_map_offset [[comment("EP map offset")]]; | |
u32 ep_entry_count [[comment("EP entry count")]]; | |
if ((stream_id & 0xF0) == 0xE0) | |
Video video [[comment("Video stream")]]; | |
else if (stream_id == 0xBD && ((private_stream_id & 0xF0) == 0 || (private_stream_id & 0xF0) == 0x10)) | |
Audio audio [[comment("Audio stream")]]; | |
else if (stream_id == 0xBD && (private_stream_id & 0xF0) == 0x20) | |
u32 other; | |
else if (stream_id == 0xBD && private_stream_id == 0x7D) | |
u32 other; | |
else if (stream_id == 0xBD && private_stream_id == 0x7E) | |
u32 other; | |
else if (stream_id != 0xBD || private_stream_id != 0x7F) | |
std::assert(false, "Stream has incorrect attributes"); | |
else | |
u32 other; | |
if (ep_map_offset != 0) | |
StreamEpMap<ep_entry_count> stream_ep_map @ Origin + ep_map_offset [[comment("EP Map section (entry points)")]]; | |
}; | |
struct GroupesInfo<auto Origin> { | |
u8 count [[comment("Number of groups (always 1)")]]; | |
u32 size [[comment("Size of groups info (excluding this and previous fields)")]]; | |
u8 unk0 [[comment("Always 0")]]; | |
u8 stream_count [[comment("Number of streams")]]; | |
StreamInfo<Origin> stream_info[stream_count]; | |
auto size_exclude = sizeof(count) + sizeof(size); | |
std::assert(size == sizeof(this) - size_exclude, "Groupes info size mismatch"); | |
}; | |
struct GroupingPeriodInfo<auto Origin> { | |
u8 count [[comment("Number of grouping periods (always 1)")]]; | |
u32 size [[comment("Size of grouping period info (excluding this and previous fields)")]]; | |
u48 presentation_start [[comment("48-bit presentation start time")]]; | |
u48 presentation_end [[comment("48-bit presentation end time")]]; | |
u8 unk0 [[comment("Always 0")]]; | |
GroupesInfo<Origin> groups_info; | |
auto size_exclude = sizeof(count) + sizeof(size); | |
std::assert(size == sizeof(this) - size_exclude, "Grouping period info size mismatch"); | |
}; | |
struct SequenceInfo<auto Origin> { | |
u32 size [[comment("Size of sequence info block (excluding this and previous fields)")]]; | |
u48 presentation_start [[comment("48-bit presentation start time")]]; | |
u48 presentation_end [[comment("48-bit presentation end time")]]; | |
u32 mux_rate_bound [[comment("MUX rate bound (0x3FFFFF bits used)")]]; | |
u32 std_delay_bound [[comment("STD delay bound (0xFFFFF bits used)")]]; | |
u8 stream_count [[comment("Number of streams")]]; | |
GroupingPeriodInfo<Origin> grouping_period_info; | |
auto size_exclude = sizeof(size); | |
std::assert(size == sizeof(this) - size_exclude, "Sequence info size mismatch"); | |
}; | |
enum MarkType : u8 { | |
CHAPTER = 0x05, | |
EVENT = 0x10, | |
}; | |
struct MarkEntry { | |
MarkType type [[comment("Type")]]; | |
u8 name_length [[comment("Length of mark name")]]; | |
u16 unk0 [[comment("Always 0")]]; | |
u48 timestamp [[comment("48-bit timestamp")]]; | |
u8 stream_id [[comment("Stream ID (0 for chapter marks)")]]; | |
u8 unk1 [[comment("Always 0")]]; | |
u32 mark_data [[comment("Mark data")]]; | |
char name[24] [[comment("Mark name (padded with zeros)")]]; | |
}; | |
struct MarkSection { | |
u32 size [[comment("Size of mark section")]]; | |
u16 count [[comment("Number of marks")]]; | |
MarkEntry mark_entries[count]; | |
std::assert(sizeof(this) == size, "Mark section size mismatch"); | |
}; | |
struct Header<auto Size> { | |
type::Magic<"PSMF"> magic; | |
Version version; | |
type::Hex<u32> data_offset [[hidden]]; | |
type::Hex<u32> data_size [[hidden]]; | |
type::Hex<u32> mark_offset [[hidden]]; | |
type::Hex<u32> mark_size [[hidden]]; | |
padding[56]; | |
SequenceInfo<std::ptr::relative_to_parent(0)> sequence_info; | |
if (mark_size > 0) { | |
// Padding instead of pointer to the start of structure | |
padding[mark_offset - sizeof(this)]; | |
MarkSection mark_section; | |
} | |
// Padding instead of pointer to the start of structure | |
padding[data_offset - sizeof(this)]; | |
type::Hex<u8> mps_data[data_size]; | |
}; | |
} | |
/// Wrapper for PSMF | |
struct PSMF<auto Size> { | |
be PSMF::Header<Size> header [[inline]]; | |
}; | |
namespace PG { | |
enum DataMagic : u32 { | |
PRX = 0x7E505350, // ~PSP | |
SCE = 0x7E534345, // ~SCE | |
ELF = 0x7F454C46, // \x7fELF | |
PSAR = 0x50534152, // PSAR | |
}; | |
namespace PRX { | |
bitfield ModuleAttr { | |
padding : 9; | |
bool MS : 1; | |
bool USB_WLAN : 1; | |
bool VSH : 1; | |
bool KERNEL : 1; | |
bool KIRK_MEMLMD_LIB : 1; | |
bool KIRK_SEMAPHORE_LIB : 1; | |
padding : 1; | |
}; | |
bitfield CompressionAttr { | |
bool COMPRESSED : 1; | |
bool ELF : 1; | |
padding : 1; | |
bool GZIP_OVERLAP : 1; | |
padding : 5; | |
bool KL4E_COMPRESSED : 1; | |
padding : 6; | |
}; | |
enum DecryptMode : u8 { | |
NO_EXEC = 0, /* Not an executable. */ | |
BOGUS_MODULE = 1, /* 1.50 Kernel module. */ | |
KERNEL_MODULE = 2, | |
VSH_MODULE = 3, | |
USER_MODULE = 4, | |
UMD_GAME_EXEC = 9, | |
GAMESHARING_EXEC = 10, | |
GAMESHARING_EXEC_DEVTOOL = 11, | |
MS_UPDATER = 12, | |
DEMO_EXEC = 13, | |
APP_MODULE = 14, | |
MS_GAME_PATCH = 18, // 0x12 | |
MS_GAME_PATCH_DEVTOOL = 19, // 0x13 | |
POPS_EXEC = 20, // 0x14 | |
UNKNOWN_21 = 21, // 0x15 | |
UNKNOWN_22 = 22, // 0x16 | |
USER_NPDRM = 23, // 0x17 | |
MS_GAME_PBOOT = 25, // 0x19 | |
}; | |
struct Header { | |
type::Magic<"~PSP"> magic; | |
PRX::ModuleAttr mod_attr [[comment("Module attributes")]]; | |
PRX::CompressionAttr comp_attr [[comment("Compression attributes")]]; | |
u8 module_ver_lo [[comment("Minor module version")]]; | |
u8 module_ver_hi [[comment("Major module version")]]; | |
std::string::NullString mod_name [[comment("Module name")]]; | |
padding[28 - sizeof(mod_name)]; // Padding to 28 bytes | |
u8 mod_version [[comment("Module version")]]; | |
u8 n_segments [[comment("Number of segments")]]; | |
type::Hex<u32> elf_size [[comment("Size of ELF (uncompressed and decrypted)")]]; | |
type::Hex<u32> psp_size [[comment("Size of PSP (compressed/encrypted)")]]; | |
type::Hex<u32> boot_entry [[comment("Boot entry address (offset from the start of .text)")]]; | |
type::Hex<u32> mod_info_offset [[comment("Offset to the module info")]]; | |
type::Hex<u32> bss_size [[comment("Size of BSS")]]; | |
type::Hex<u16> seg_align[4] [[comment("Alignment info of each segment")]]; | |
type::Hex<u32> seg_addr[4] [[comment("Start address of each segment")]]; | |
type::Hex<u32> seg_size[4] [[comment("Size of each segment")]]; | |
padding[4*5]; // TODO: Unknown | |
u32 devkit_version [[comment("DevKit version")]]; | |
PRX::DecryptMode decrypt_mode [[comment("Decrypt mode")]]; | |
padding[1]; // TODO: Unknown | |
type::Hex<u16> overlap_size [[comment("Size of the overlap")]]; | |
type::Hex<u8> aes_key[0x10] [[comment("AES key")]]; | |
type::Hex<u8> cmac_key[0x10] [[comment("CMAC key")]]; | |
type::Hex<u8> cmac_hdr_hash[0x10] [[comment("CMAC header hash")]]; | |
type::Hex<u32> comp_size [[comment("Size of the compressed ELF")]]; | |
padding[4*3]; // TODO: Unknown | |
type::Hex<u8> cmac_data_hash[0x10] [[comment("CMAC data hash")]]; | |
type::Hex<u32> tag [[comment("Tag")]]; | |
type::Hex<u8> s_check[0x58] [[comment("Check")]]; | |
type::Hex<u8> sha1_hash[0x14] [[comment("SHA1 hash")]]; | |
type::Hex<u8> key_data4[0x10] [[comment("Key data")]]; | |
std::assert(sizeof(this) == 0x150, "PRX Header size mismatch"); | |
}; | |
} | |
struct PRX<auto Size> { | |
PRX::Header header; | |
type::Hex<u8> data[header.psp_size - sizeof(this)]; | |
std::assert(sizeof(this) == Size, "PRX size mismatch"); | |
}; | |
namespace SCE { | |
struct Header<auto Size> { | |
type::Magic<"~SCE"> magic; | |
type::Hex<u32> hdr_size [[hidden]]; | |
type::Hex<u8> hdr_version [[hidden]]; | |
padding[4*13]; // TODO: Unknown | |
std::assert(hdr_size == sizeof(this), "SCE Header size mismatch"); | |
PRX<Size - hdr_size> prx; | |
}; | |
} | |
struct SCE<auto Size> { | |
SCE::Header<Size> header [[inline]]; | |
std::assert(sizeof(this) == Size, "SCE size mismatch"); | |
}; | |
namespace PSAR { | |
struct Version { | |
type::Hex<u16> lo; | |
type::Hex<u16> hi; | |
}; | |
} | |
struct PSAR<auto Size> { | |
type::Magic<"PSAR"> magic; | |
PSAR::Version version; | |
type::Hex<u32> data_size; | |
type::Hex<u32> unk; | |
type::Hex<u8> data[data_size]; | |
// TODO: Expected size have 0x10 bytes more, figure out why | |
//std::assert(sizeof(this) == Size, "PSAR size mismatch"); | |
}; | |
} | |
struct PG<auto Size> { | |
// Get magic and reset $ to the beginning of the structure | |
be PG::DataMagic magic [[hidden]]; | |
$ = std::ptr::relative_to_parent(0); | |
match (magic) { | |
(PG::DataMagic::PRX ): le PG::PRX<Size> prx; | |
(PG::DataMagic::SCE ): le PG::SCE<Size> sce; | |
(PG::DataMagic::ELF ): le ELF<Size> elf; | |
(PG::DataMagic::PSAR): le PG::PSAR<Size> psar; | |
(_): type::Hex<u8> other[Size - sizeof(this)]; | |
} | |
}; | |
struct PBP { | |
type::Magic<"\x00PBP"> magic; | |
type::BCD<4> version; | |
// Get offsets | |
type::Hex<u32> param_sfo_offset [[hidden]]; | |
type::Hex<u32> icon0_offset [[hidden]]; | |
type::Hex<u32> icon1_offset [[hidden]]; | |
type::Hex<u32> pic0_offset [[hidden]]; | |
type::Hex<u32> pic1_offset [[hidden]]; | |
type::Hex<u32> snd0_offset [[hidden]]; | |
type::Hex<u32> boot_pg_offset [[hidden]]; | |
type::Hex<u32> pg_data_offset [[hidden]]; | |
// Get sizes | |
u32 param_sfo_size = icon0_offset - param_sfo_offset; | |
u32 icon0_size = icon1_offset - icon0_offset; | |
u32 icon1_size = pic0_offset - icon1_offset; | |
u32 pic0_size = pic1_offset - pic0_offset; | |
u32 pic1_size = snd0_offset - pic1_offset; | |
u32 snd0_size = boot_pg_offset - snd0_offset; | |
u32 boot_pg_size = pg_data_offset - boot_pg_offset; | |
u32 pg_data_size = std::ptr::relative_to_end(0) - pg_data_offset; | |
// Evaluate | |
PSF<param_sfo_size> param_sfo; | |
if (icon0_size > 0) PNG<icon0_size> icon0 @ icon0_offset; | |
if (icon1_size > 0) PSMF<icon1_size> icon1 @ icon1_offset; | |
if (pic0_size > 0) PNG<pic0_size> pic0 @ pic0_offset; | |
if (pic1_size > 0) PNG<pic1_size> pic1 @ pic1_offset; | |
if (snd0_size > 0) WAV<snd0_size> snd0 @ snd0_offset; | |
if (boot_pg_size > 0) PG<boot_pg_size> boot_pg @ boot_pg_offset; | |
if (pg_data_size > 0) PG<pg_data_size> pg_data @ pg_data_offset; | |
// Add virtual files | |
hex::core::add_virtual_file("PARAM.SFO", param_sfo); | |
if (icon0_size > 0) hex::core::add_virtual_file("ICON0.PNG", icon0); | |
if (icon1_size > 0) hex::core::add_virtual_file("ICON1.PMF", icon1); | |
if (pic0_size > 0) hex::core::add_virtual_file("PIC0.PNG", pic0); | |
if (pic1_size > 0) hex::core::add_virtual_file("PIC1.PNG", pic1); | |
if (snd0_size > 0) hex::core::add_virtual_file("SND0.AT3", snd0); | |
if (boot_pg_size > 0) hex::core::add_virtual_file("DATA.PSP", boot_pg); | |
if (pg_data_size > 0) hex::core::add_virtual_file("DATA.PSAR", pg_data); | |
}; | |
le PBP pbp @ $ [[inline]]; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment