diff --git a/README.md b/README.md index b52e5913..81f726fc 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,7 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi | DDS | `image/vnd-ms.dds` | [`patterns/dds.hexpat`](patterns/dds.hexpat) | DirectDraw Surface | | DEX | | [`patterns/dex.hexpat`](patterns/dex.hexpat) | Dalvik EXecutable Format | | DICOM | `application/dicom` | [`patterns/dicom.hexpat`](patterns/dicom.hexpat) | DICOM image format | +| DLS | | [`patterns/dls.hexpat`](patterns/dls.hexpat) | Downloadable Sounds (DLS) | | DMG | | [`patterns/dmg.hexpat`](patterns/dmg.hexpat) | Apple Disk Image Trailer (DMG) | | DMP | | [`patterns/dmp64.hexpat`](patterns/dmp64.hexpat) | Windows Kernel Dump(DMP64) | | DPAPI_Blob | | [`patterns/dpapblob.hexpat`](patterns/dpapiblob.hexpat) | Data protection API Blob File Format | @@ -189,6 +190,7 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi | XGT | | [`patterns/xgt.hexpat`](patterns/xgstexture.hexpat) | Exient XGS Engine Texture | | Xilinx BIT | | [`patterns/xilinx_bit.hexpat`](patterns/xilinx_bit.hexpat) | Xilinx FPGA Bitstreams | | Xilinx Bootgen | `application/x-xilinx-boot-zynqmp` | [`patterns/xilinx_bootgen.hexpat`](patterns/xilinx_bootgen.hexpat) | Xilinx ZynqMP Boot Images | +| XMF | `audio/mobile-xmf` | [`patterns/xmf.hexpat`](patterns/xmf.hexpat) | MMA's XMF (eXtensible Music Format) and MXMF (Mobile eXtensible Music Format) | | ZIM | | [`patterns/zim.hexpat`](patterns/zim.hexpat) | [ZIM](https://openzim.org) file format | | ZIP | `application/zip` | [`patterns/zip.hexpat`](patterns/zip.hexpat) | End of Central Directory Header, Central Directory File Headers | | ZLIB | `application/zlib` | [`patterns/zlib.hexpat`](patterns/zlib.hexpat) | ZLIB compressed data format | diff --git a/includes/type/mmreg.pat b/includes/type/mmreg.pat new file mode 100644 index 00000000..5c177f62 --- /dev/null +++ b/includes/type/mmreg.pat @@ -0,0 +1,379 @@ +import type.guid; + +/*! + Microsoft's mmreg.h +*/ + +namespace auto type { + + enum TypewFormatTag : u16 { + WAVE_FORMAT_PCM = 0x0001, // Microsoft PCM Format + WAVE_FORMAT_ADPCM = 0x0002, // Microsoft ADPCM Format + WAVE_FORMAT_IEEE_FLOAT = 0x0003, // IEEE float + WAVE_FORMAT_VSELP = 0x0004, // Compaq Computer's VSELP + WAVE_FORMAT_IBM_CVSD = 0x0005, // IBM CVSD + WAVE_FORMAT_ALAW = 0x0006, // ALAW + WAVE_FORMAT_MULAW = 0x0007, // MULAW + WAVE_FORMAT_DTS = 0x0008, // Digital Theater Systems DTS + WAVE_FORMAT_DRM = 0x0009, // Microsoft Corporation + WAVE_FORMAT_WMAVOICE9 = 0x000A, // Microsoft Corporation + WAVE_FORMAT_WMAVOICE10 = 0x000B, // Microsoft Corporation + WAVE_FORMAT_OKI_ADPCM = 0x0010, // OKI ADPCM + // WAVE_FORMAT_DVI_ADPCM = 0x0011, // Intel's DVI ADPCM + WAVE_FORMAT_IMA_ADPCM = 0x0011, // Intel's DVI ADPCM + WAVE_FORMAT_MEDIASPACE_ADPCM = 0x0012, // Videologic's MediaSpace ADPCM + WAVE_FORMAT_SIERRA_ADPCM = 0x0013, // Sierra ADPCM + WAVE_FORMAT_G723_ADPCM = 0x0014, // G.723 ADPCM + WAVE_FORMAT_DIGISTD = 0x0015, // DSP Solution's DIGISTD + WAVE_FORMAT_DIGIFIX = 0x0016, // DSP Solution's DIGIFIX + WAVE_FORMAT_DIALOGIC_OKI_ADPCM = 0x0017, // Dialogic Corporation + WAVE_FORMAT_MEDIAVISION_ADPCM = 0x0018, // Media Vision ADPCM + WAVE_FORMAT_YAMAHA_ADPCM = 0x0020, // Yamaha ADPCM + WAVE_FORMAT_SONARC = 0x0021, // Speech Compression's Sonarc + WAVE_FORMAT_TRUESPEECH = 0x0022, // DSP Group's True Speech + WAVE_FORMAT_ECHOSC1 = 0x0023, // Echo Speech's EchoSC1 + WAVE_FORMAT_AUDIOFILE_AF36 = 0x0024, // Audiofile AF36 + WAVE_FORMAT_APTX = 0x0025, // APTX + WAVE_FORMAT_AUDIOFILE_AF10 = 0x0026, // AudioFile AF10 + WAVE_FORMAT_PROSODY_1612 = 0x0027, // Prosody 1612 + WAVE_FORMAT_LRC = 0x0028, // LRC + WAVE_FORMAT_DOLBY_AC2 = 0x0030, // Dolby AC2 + WAVE_FORMAT_GSM610 = 0x0031, // GSM610 + WAVE_FORMAT_MSNAUDIO = 0x0032, // MSNAudio + WAVE_FORMAT_ANTEX_ADPCME = 0x0033, // Antex ADPCME + WAVE_FORMAT_CONTROL_RES_VQLPC = 0x0034, // Control Res VQLPC + WAVE_FORMAT_DIGIREAL = 0x0035, // Digireal + WAVE_FORMAT_DIGIADPCM = 0x0036, // DigiADPCM + WAVE_FORMAT_CONTROL_RES_CR10 = 0x0037, // Control Res CR10 + WAVE_FORMAT_NMS_VBXADPCM = 0x0038, // NMS VBXADPCM + WAVE_FORMAT_CS_IMAADPCM = 0x0039, // CS IMAADPCM + WAVE_FORMAT_ECHOSC3 = 0x003A, // EchoSC3 + WAVE_FORMAT_ROCKWELL_ADPCM = 0x003B, // Rockwell ADPCM + WAVE_FORMAT_ROCKWELL_DIGITALK = 0x003C, // Rockwell Digit LK + WAVE_FORMAT_XEBEC = 0x003D, // Xebec + WAVE_FORMAT_G721_ADPCM = 0x0040, // Antex Electronics Corporation + WAVE_FORMAT_G728_CELP = 0x0041, // G.728 CELP + WAVE_FORMAT_MSG723 = 0x0042, // MSG723 + WAVE_FORMAT_MPEG = 0x0050, // MPEG Layer 1, 2 + WAVE_FORMAT_RT24 = 0x0052, // RT24 + WAVE_FORMAT_PAC = 0x0053, // PAC + WAVE_FORMAT_MPEGLAYER3 = 0x0055, // MPEG Layer 3 + WAVE_FORMAT_LUCENT_G723 = 0x0059, // Lucent Technologies + WAVE_FORMAT_CIRRUS = 0x0060, // Cirrus Logic + WAVE_FORMAT_ESPCM = 0x0061, // ESS Technology + WAVE_FORMAT_VOXWARE = 0x0062, // Voxware Inc. + WAVE_FORMAT_CANOPUS_ATRAC = 0x0063, // Canopus, co., Ltd. + WAVE_FORMAT_G726_ADPCM = 0x0064, // APICOM + WAVE_FORMAT_G722_ADPCM = 0x0065, // APICOM + WAVE_FORMAT_DSAT = 0x0066, // Microsoft Corporation + WAVE_FORMAT_DSAT_DISPLAY = 0x0067, // Microsoft Corporation + WAVE_FORMAT_VOXWARE_BYTE_ALIGNED = 0x0069, // Voxware Inc. + WAVE_FORMAT_VOXWARE_AC8 = 0x0070, // Voxware Inc. + WAVE_FORMAT_VOXWARE_AC10 = 0x0071, // Voxware Inc. + WAVE_FORMAT_VOXWARE_AC16 = 0x0072, // Voxware Inc. + WAVE_FORMAT_VOXWARE_AC20 = 0x0073, // Voxware Inc. + WAVE_FORMAT_VOXWARE_RT24 = 0x0074, // Voxware Inc. + WAVE_FORMAT_VOXWARE_RT29 = 0x0075, // Voxware Inc. + WAVE_FORMAT_VOXWARE_RT29HW = 0x0076, // Voxware Inc. + WAVE_FORMAT_VOXWARE_VR12 = 0x0077, // Voxware Inc. + WAVE_FORMAT_VOXWARE_VR18 = 0x0078, // Voxware Inc. + WAVE_FORMAT_VOXWARE_TQ40 = 0x0079, // Voxware Inc. + WAVE_FORMAT_SOFTSOUND = 0x0080, // Softsound, Ltd. + WAVE_FORMAT_VOXWARE_TQ60 = 0x0081, // Voxware Inc. + WAVE_FORMAT_MSRT24 = 0x0082, // Microsoft Corporation + WAVE_FORMAT_G729A = 0x0083, // AT&T Labs, Inc. + WAVE_FORMAT_MVI_MVI2 = 0x0084, // Motion Pixels + WAVE_FORMAT_DF_G726 = 0x0085, // DataFusion Systems (Pty) (Ltd) + WAVE_FORMAT_DF_GSM610 = 0x0086, // DataFusion Systems (Pty) (Ltd) + WAVE_FORMAT_ISIAUDIO = 0x0088, // Iterated Systems, Inc. + WAVE_FORMAT_ONLIVE = 0x0089, // OnLive! Technologies, Inc. + WAVE_FORMAT_SBC24 = 0x0091, // Siemens Business Communications Sys + WAVE_FORMAT_DOLBY_AC3_SPDIF = 0x0092, // Sonic Foundry + WAVE_FORMAT_MEDIASONIC_G723 = 0x0093, // MediaSonic + WAVE_FORMAT_PROSODY_8KBPS = 0x0094, // Aculab + WAVE_FORMAT_ZYXEL_ADPCM = 0x0097, // ZyXEL Communications, Inc. + WAVE_FORMAT_PHILIPS_LPCBB = 0x0098, // Philips Speech Processing + WAVE_FORMAT_PACKED = 0x0099, // Studer Professional Audio AG + WAVE_FORMAT_MALDEN_PHONYTALK = 0x00A0, // Malden Electronics Ltd. + WAVE_FORMAT_RACAL_RECORDER_GSM = 0x00A1, // Racal recorders + WAVE_FORMAT_RACAL_RECORDER_G720_A = 0x00A2, // Racal recorders + WAVE_FORMAT_RACAL_RECORDER_G723_1 = 0x00A3, // Racal recorders + WAVE_FORMAT_RACAL_RECORDER_TETRA_ACELP = 0x00A4, // Racal recorders + WAVE_FORMAT_NEC_AAC = 0x00B0, // NEC Corp. + WAVE_FORMAT_RAW_AAC1 = 0x00FF, // For Raw AAC, with format block AudioSpecificConfig() (as defined by MPEG-4) + WAVE_FORMAT_RHETOREX_ADPCM = 0x0100, // Rhetorex Inc. + WAVE_FORMAT_IRAT = 0x0101, // BeCubed Software Inc. + WAVE_FORMAT_VIVO_G723 = 0x0111, // Vivo Software + WAVE_FORMAT_VIVO_SIREN = 0x0112, // Vivo Software + WAVE_FORMAT_PHILIPS_CELP = 0x0120, // Philips Speech Processing + WAVE_FORMAT_PHILIPS_GRUNDIG = 0x0121, // Philips Speech Processing + WAVE_FORMAT_DIGITAL_G723 = 0x0123, // Digital Equipment Corporation + WAVE_FORMAT_SANYO_LD_ADPCM = 0x0125, // Sanyo Electric Co., Ltd. + WAVE_FORMAT_SIPROLAB_ACEPLNET = 0x0130, // Sipro Lab Telecom Inc. + WAVE_FORMAT_SIPROLAB_ACELP4800 = 0x0131, // Sipro Lab Telecom Inc. + WAVE_FORMAT_SIPROLAB_ACELP8V3 = 0x0132, // Sipro Lab Telecom Inc. + WAVE_FORMAT_SIPROLAB_G729 = 0x0133, // Sipro Lab Telecom Inc. + WAVE_FORMAT_SIPROLAB_G729A = 0x0134, // Sipro Lab Telecom Inc. + WAVE_FORMAT_SIPROLAB_KELVIN = 0x0135, // Sipro Lab Telecom Inc. + WAVE_FORMAT_VOICEAGE_AMR = 0x0136, // VoiceAge Corp. + WAVE_FORMAT_G726ADPCM = 0x0140, // Dictaphone Corporation + WAVE_FORMAT_DICTAPHONE_CELP68 = 0x0141, // Dictaphone Corporation + WAVE_FORMAT_DICTAPHONE_CELP54 = 0x0142, // Dictaphone Corporation + WAVE_FORMAT_QUALCOMM_PUREVOICE = 0x0150, // Qualcomm, Inc. + WAVE_FORMAT_QUALCOMM_HALFRATE = 0x0151, // Qualcomm, Inc. + WAVE_FORMAT_TUBGSM = 0x0155, // Ring Zero Systems, Inc. + WAVE_FORMAT_MSAUDIO1 = 0x0160, // Microsoft Corporation + WAVE_FORMAT_WMAUDIO2 = 0x0161, // Microsoft Corporation + WAVE_FORMAT_WMAUDIO3 = 0x0162, // Microsoft Corporation + WAVE_FORMAT_WMAUDIO_LOSSLESS = 0x0163, // Microsoft Corporation + WAVE_FORMAT_WMASPDIF = 0x0164, // Microsoft Corporation + WAVE_FORMAT_UNISYS_NAP_ADPCM = 0x0170, // Unisys Corp. + WAVE_FORMAT_UNISYS_NAP_ULAW = 0x0171, // Unisys Corp. + WAVE_FORMAT_UNISYS_NAP_ALAW = 0x0172, // Unisys Corp. + WAVE_FORMAT_UNISYS_NAP_16K = 0x0173, // Unisys Corp. + WAVE_FORMAT_CREATIVE_ADPCM = 0x0200, // Creative Labs, Inc + WAVE_FORMAT_CREATIVE_FASTSPEECH8 = 0x0202, // Creative Labs, Inc + WAVE_FORMAT_CREATIVE_FASTSPEECH10 = 0x0203, // Creative Labs, Inc + WAVE_FORMAT_UHER_ADPCM = 0x0210, // UHER informatic GmbH + WAVE_FORMAT_QUARTERDECK = 0x0220, // Quarterdeck Corporation + WAVE_FORMAT_ILINK_VC = 0x0230, // I-link Worldwide + WAVE_FORMAT_RAW_SPORT = 0x0240, // Aureal Semiconductor + WAVE_FORMAT_ESST_AC3 = 0x0241, // ESS Technology, Inc. + WAVE_FORMAT_IPI_HSX = 0x0250, // Interactive Products, Inc. + WAVE_FORMAT_IPI_RPELP = 0x0251, // Interactive Products, Inc. + WAVE_FORMAT_CS2 = 0x0260, // Consistent Software + WAVE_FORMAT_SONY_SCX = 0x0270, // Sony Corp. + WAVE_FORMAT_FM_TOWNS_SND = 0x0300, // Fujitsu Corp. + WAVE_FORMAT_BTV_DIGITAL = 0x0400, // Brooktree Corporation + WAVE_FORMAT_QDESIGN_MUSIC = 0x0450, // QDesign Corporation + WAVE_FORMAT_VME_VMPCM = 0x0680, // AT&T Labs, Inc. + WAVE_FORMAT_TPC = 0x0681, // AT&T Labs, Inc. + WAVE_FORMAT_OLIGSM = 0x1000, // Ing C. Olivetti & C., S.p.A. + WAVE_FORMAT_OLIADPCM = 0x1001, // Ing C. Olivetti & C., S.p.A. + WAVE_FORMAT_OLICELP = 0x1002, // Ing C. Olivetti & C., S.p.A. + WAVE_FORMAT_OLISBC = 0x1003, // Ing C. Olivetti & C., S.p.A. + WAVE_FORMAT_OLIOPR = 0x1004, // Ing C. Olivetti & C., S.p.A. + WAVE_FORMAT_LH_CODEC = 0x1100, // Lernout & Hauspie + WAVE_FORMAT_LH_CODEC_CELP = 0x1101, // Lernout & Hauspie + WAVE_FORMAT_LH_CODEC_SBC8 = 0x1102, // Lernout & Hauspie + WAVE_FORMAT_LH_CODEC_SBC12 = 0x1103, // Lernout & Hauspie + WAVE_FORMAT_LH_CODEC_SBC16 = 0x1104, // Lernout & Hauspie + WAVE_FORMAT_NORRIS = 0x1400, // Norris Communications, Inc. + WAVE_FORMAT_ISIAUDIO_2 = 0x1401, // ISIAudio + WAVE_FORMAT_SOUNDSPACE_MUSICOMPRESS = 0x1500, // AT&T Labs, Inc. + WAVE_FORMAT_MPEG_ADTS_AAC = 0x1600, // Microsoft Corporation + WAVE_FORMAT_MPEG_RAW_AAC = 0x1601, // Microsoft Corporation + WAVE_FORMAT_MPEG_LOAS = 0x1602, // Microsoft Corporation + WAVE_FORMAT_NOKIA_MPEG_ADTS_AAC = 0x1608, // Microsoft Corporation + WAVE_FORMAT_NOKIA_MPEG_RAW_AAC = 0x1609, // Microsoft Corporation + WAVE_FORMAT_VODAFONE_MPEG_ADTS_AAC = 0x160A, // Microsoft Corporation + WAVE_FORMAT_VODAFONE_MPEG_RAW_AAC = 0x160B, // Microsoft Corporation + WAVE_FORMAT_MPEG_HEAAC = 0x1610, // Microsoft Corporation + WAVE_FORMAT_VOXWARE_RT24_SPEECH = 0x181C, // Voxware Inc. + WAVE_FORMAT_SONICFOUNDRY_LOSSLESS = 0x1971, // Sonic Foundry + WAVE_FORMAT_INNINGS_TELECOM_ADPCM = 0x1979, // Innings Telecom Inc. + WAVE_FORMAT_LUCENT_SX8300P = 0x1C07, // Lucent Technologies + WAVE_FORMAT_LUCENT_SX5363S = 0x1C0C, // Lucent Technologies + WAVE_FORMAT_CUSEEME = 0x1F03, // CUSeeMe + WAVE_FORMAT_NTCSOFT_ALF2CM_ACM = 0x1FC4, // NTCSoft + WAVE_FORMAT_DVM = 0x2000, // FAST Multimedia AG + WAVE_FORMAT_DTS2 = 0x2001, // FAST Multimedia AG + WAVE_FORMAT_MAKEAVIS = 0x3313, // MakeAVIs + WAVE_FORMAT_DIVIO_MPEG4_AAC = 0x4143, // Divio, Inc. + WAVE_FORMAT_NOKIA_ADAPTIVE_MULTIRATE = 0x4201, // Nokia + WAVE_FORMAT_DIVIO_G726 = 0x4243, // Divio, Inc. + WAVE_FORMAT_LEAD_SPEECH = 0x434C, // LEAD Technologies + WAVE_FORMAT_LEAD_VORBIS = 0x564C, // LEAD Technologies + WAVE_FORMAT_WAVPACK_AUDIO = 0x5756, // xiph.org + WAVE_FORMAT_OGGVORBIS_MODE_1 = 0x674F, // Xiph.Org Foundation + WAVE_FORMAT_OGGVORBIS_MODE_2 = 0x6750, // Xiph.Org Foundation + WAVE_FORMAT_OGGVORBIS_MODE_3 = 0x6751, // Xiph.Org Foundation + WAVE_FORMAT_OGGVORBIS_MODE_1_PLUS = 0x676F, // Xiph.Org Foundation + WAVE_FORMAT_OGGVORBIS_MODE_2_PLUS = 0x6770, // Xiph.Org Foundation + WAVE_FORMAT_OGGVORBIS_MODE_3_PLUS = 0x6771, // Xiph.Org Foundation + WAVE_FORMAT_3COM_NBX = 0x7000, // 3COM Corporation + WAVE_FORMAT_OPUS = 0x704F, // Xiph.Org Foundation + WAVE_FORMAT_FAAD_AAC = 0x706D, // Freeware Advanced Audio Coder + WAVE_FORMAT_AMR_NB = 0x7361, // 3GPP + WAVE_FORMAT_AMR_WB = 0x7362, // 3GPP + WAVE_FORMAT_AMR_WP = 0x7363, // 3GPP + WAVE_FORMAT_GSM_AMR_CBR = 0x7A21, // GSMA/3GPP + WAVE_FORMAT_GSM_AMR_VBR_SID = 0x7A22, // GSMA/3GPP + WAVE_FORMAT_COMVERSE_INFOSYS_G723_1 = 0xA100, // Comverse Infosys + WAVE_FORMAT_COMVERSE_INFOSYS_AVQSBC = 0xA101, // Comverse Infosys + WAVE_FORMAT_COMVERSE_INFOSYS_SBC = 0xA102, // Comverse Infosys + WAVE_FORMAT_SYMBOL_G729_A = 0xA103, // Symbol Technologies + WAVE_FORMAT_VOICEAGE_AMR_WB = 0xA104, // VoiceAge Corp. + WAVE_FORMAT_INGENIENT_G726 = 0xA105, // Ingenient Technologies, Inc. + WAVE_FORMAT_MPEG4_AAC = 0xA106, // ISO/MPEG-4 + WAVE_FORMAT_ENCORE_G726 = 0xA107, // Encore Software + WAVE_FORMAT_ZOLL_ASAO = 0xA108, // ZOLL Medical Corporation + WAVE_FORMAT_SPEEX_VOICE = 0xA109, // xiph.org + WAVE_FORMAT_VIANIX_MASC = 0xA10A, // Vianix LLC + WAVE_FORMAT_WM9_SPECTRUM_ANALYZER = 0xA10B, // Microsoft + WAVE_FORMAT_WMF_SPECTRUM_ANAYZER = 0xA10C, // Microsoft + WAVE_FORMAT_GSM_610 = 0xA10D, // Microsoft + WAVE_FORMAT_GSM_620 = 0xA10E, // Microsoft + WAVE_FORMAT_GSM_660 = 0xA10F, // Microsoft + WAVE_FORMAT_GSM_690 = 0xA110, // Microsoft + WAVE_FORMAT_GSM_ADAPTIVE_MULTIRATE_WB = 0xA111, // Microsoft + WAVE_FORMAT_POLYCOM_G722 = 0xA112, // Polycom + WAVE_FORMAT_POLYCOM_G728 = 0xA113, // Polycom + WAVE_FORMAT_POLYCOM_G729_A = 0xA114, // Polycom + WAVE_FORMAT_POLYCOM_SIREN = 0xA115, // Polycom + WAVE_FORMAT_GLOBAL_IP_ILBC = 0xA116, // Global IP Sound + WAVE_FORMAT_RADIOTIME_TIME_SHIFT_RADIO = 0xA117, // RadioTime + WAVE_FORMAT_NICE_ACA = 0xA118, // NICE Systems + WAVE_FORMAT_NICE_ADPCM = 0xA119, // NICE Systems + WAVE_FORMAT_VOCORD_G721 = 0xA11A, // Vocord Telecom + WAVE_FORMAT_VOCORD_G726 = 0xA11B, // Vocord Telecom + WAVE_FORMAT_VOCORD_G722_1 = 0xA11C, // Vocord Telecom + WAVE_FORMAT_VOCORD_G728 = 0xA11D, // Vocord Telecom + WAVE_FORMAT_VOCORD_G729 = 0xA11E, // Vocord Telecom + WAVE_FORMAT_VOCORD_G729_A = 0xA11F, // Vocord Telecom + WAVE_FORMAT_VOCORD_G723_1 = 0xA120, // Vocord Telecom + WAVE_FORMAT_VOCORD_LBC = 0xA121, // Vocord Telecom + WAVE_FORMAT_NICE_G728 = 0xA122, // NICE Systems + WAVE_FORMAT_FRACE_TELECOM_G729 = 0xA123, // France Telecom + WAVE_FORMAT_CODIAN = 0xA124, // CODIAN + WAVE_FORMAT_FLAC = 0xF1AC, // xiph.org + WAVE_FORMAT_EXTENSIBLE = 0xFFFE, // Microsoft + WAVE_FORMAT_DEVELOPMENT = 0xFFFF // Microsoft + }; + + struct WAVEFORMAT { + TypewFormatTag wFormatTag; + u16 wChannels; + u32 dwSamplesPerSec [[comment("Sample rate")]]; + u32 dwAvgBytesPerSec [[comment("Average bytes per second")]]; + u16 wBlockAlign; + } [[static]]; + + struct WAVEFORMATEX { + TypewFormatTag wFormatTag; + u16 wChannels; + u32 dwSamplesPerSec [[comment("Sample rate")]]; + u32 dwAvgBytesPerSec [[comment("Average bytes per second")]]; + u16 wBlockAlign; + u16 wBitsPerSample; + u16 cbSize; + } [[static]]; + + struct PCMWAVEFORMAT { + WAVEFORMAT wf [[inline]]; + u16 wBitsPerSample; + } [[static]]; + + struct ADPCMOffest { + u16 iCoef1; + u16 iCoef2; + } [[static]]; + + struct ADPCMWAVEFORMAT { + WAVEFORMATEX wfx [[inline]]; + u16 wSamplesPerBlock; + u16 wNumCoef; + ADPCMOffest aCoef[wNumCoef]; + }; + + enum WaveFormatMPEGLayer3Flags : u32 { + MPEGLAYER3_FLAG_PADDING_ISO, + MPEGLAYER3_FLAG_PADDING_ON, + MPEGLAYER3_FLAG_PADDING_OFF + }; + + enum WaveFormatMPEGLayer3IDs : u16 { + MPEGLAYER3_ID_UNKNOWN, + MPEGLAYER3_ID_MPEG, + MPEGLAYER3_ID_CONSTANTFRAMESIZE + }; + + struct MPEGLAYER3WAVEFORMAT { + WAVEFORMATEX wfx [[inline]]; + WaveFormatMPEGLayer3IDs id; + WaveFormatMPEGLayer3Flags flags; + u16 blockSize; + u16 framesPerBlock; + u16 codecDelay; + } [[static]]; + + 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 MPEG1WAVEFORMAT { + WAVEFORMATEX wfx [[inline]]; + WaveMPEGLayer headLayersUsed; + u32 headBitrate; + WaveMPEGMode headMode; + u16 headModeExt; + u16 headEmphasis; + WaveMPEGFlags headFlags; + u32 PTSLow; + u32 PTSHigh; + } [[static]]; + + bitfield WlnkChannel { + bool LeftMono : 1; + bool Right : 1; + bool Center : 1; + bool LFE : 1; + bool LeftSurround : 1; + bool RightSurround : 1; + bool LeftCenter : 1; + bool RightCenter : 1; + bool SurroundCenter : 1; + bool SideLeft : 1; + bool SideRight : 1; + bool Top : 1; + bool TopFL : 1; + bool TopFC : 1; + bool TopFR : 1; + bool TopRL : 1; + bool TopRC : 1; + bool TopRR : 1; + padding : 14; + }; + + namespace impl { + union WAVEFORMATEXTENSIBLEwFIELD { + u16 wValidBitsPerSample; + u16 wSamplesPerBlock; // when wBitsPerSample == 0 + u16 wReserved; + }; + } + + struct WAVEFORMATEXTENSIBLE { + WAVEFORMATEX wfx [[inline]]; + impl::WAVEFORMATEXTENSIBLEwFIELD wField; + WlnkChannel dwChannelMask; + GUID subFormat; + } [[static]]; + + using WAVEFORMATIEEEFLOATEX = WAVEFORMATEXTENSIBLE; + + struct IMAADPCMWAVEFORMAT { + WAVEFORMATEX wfx [[inline]]; + u16 wSamplesPerBlock; + } [[static]]; +} \ No newline at end of file diff --git a/patterns/dls.hexpat b/patterns/dls.hexpat new file mode 100644 index 00000000..c8a51b70 --- /dev/null +++ b/patterns/dls.hexpat @@ -0,0 +1,419 @@ +#pragma description Downloadable Sounds (DLS) +#pragma pattern_limit 4294967295 + +// DLS does not have its MIME type? +#pragma MIME application/x-riff + +import std.mem; +import std.core; +import type.guid; +import type.mmreg; + +enum ChunkID : u32 { + RIFF = 0x46464952, + LIST = 0x5453494C, + + INFO = 0x4F464E49, // only as LIST type + IARL = 0x4C524149, // Archival Location + IART = 0x54524149, // Artist + ICMS = 0x534D4349, // Commissioned + ICMT = 0x544D4349, // Comments + ICOP = 0x504F4349, // Copyright + ICRD = 0x44524349, // Creation Date + IENG = 0x474E4549, // Engineer + IGNR = 0x524E4749, // Genre + IKEY = 0x59454B49, // Keywords + IMED = 0x44454D49, // Medium + INAM = 0x4D414E49, // Name + IPRD = 0x44525049, // Product + ISBJ = 0x4A425349, // Subject + ISFT = 0x54464E49, // Software + ISRC = 0x43525349, // Source + ISRF = 0x46525349, // Source Form + ITCH = 0x48435449, // Technician + + DLS = 0x20534C44, // only as identifier + colh = 0x686C6F63, + dlid = 0x64696C64, + insh = 0x68736E69, + rgnh = 0x686E6772, + art1 = 0x31747261, + art2 = 0x32747261, + wlnk = 0x6B6E6C77, + wsmp = 0x706D7377, + ptbl = 0x6C627470, + vers = 0x73726576, + fmt = 0x20746D66, + data = 0x61746164, + cdl = 0x206C6463, + + cue = 0x20657563, + labl = 0x6C62616C, + note = 0x65746F6E, + fact = 0x74636166, + + // LIST types + wvpl = 0x6C707677, + wave = 0x65766177, + lins = 0x736E696C, + ins = 0x20736E69, + lrgn = 0x6E67726C, + rgn = 0x206E6772, + rgn2 = 0x326E6772, + lart = 0x7472616C, + lar2 = 0x3272616C, + + adtl = 0x6C746461, +}; + +enum ConnSource : u16 { + // Generic Sources + None = 0x0000, + LFO = 0x0001, + KeyOnVelocity = 0x0002, + KeyNumber = 0x0003, + EG1 = 0x0004, // Envelope Generator 1 + EG2 = 0x0005, // Envelope Generator 2 + PitchWheel = 0x0006, + PolyPressure = 0x0007, + ChannelPressure = 0x0008, + VibratoLFO = 0x0009, + + // MIDI Sources + CC1 = 0x0081, // Modulation Wheel + CC7 = 0x0087, // Channel Volume + CC10 = 0x008a, // Pan + CC11 = 0x008b, // Expression + CC91 = 0x00db, // Reverb Send + CC93 = 0x00dd, // Chorus Send + + // Registered Parameter Numbers + RPN0 = 0x0100, // Pitch Bend Sensitivity + RPN1 = 0x0101, // Fine Tune + RPN2 = 0x0102 // Coarse Tune +}; + +enum ConnDestination : u16 { + // Generic Destinations + None = 0x0000, + Attenuation = 0x0001, + Reserved = 0x0002, + Pitch = 0x0003, + Pan = 0x0004, + KeyNumber = 0x0005, + + // Channel Output Destinations + Left = 0x0010, + Right = 0x0011, + Center = 0x0012, + LFE = 0x0013, + LeftRear = 0x0014, + RightRear = 0x0015, + Chorus = 0x0080, + Reverb = 0x0081, + + // LFO Destinations + LFOFrequency = 0x0104, + LFOStartDelay = 0x0105, + + // EG1 Destinations + EG1AttackTime = 0x0206, + EG1DecayTime = 0x0207, + EG1Reserved = 0x0208, + EG1ReleaseTime = 0x0209, + EG1SustainLevel = 0x020a, + EG1DelayTime = 0x020b, + EG1HoldTime = 0x020c, + EG1ShutdownTime = 0x020d, + + // EG2 Destinations + EG2AttackTime = 0x030a, + EG2DecayTime = 0x030b, + EG2Reserved = 0x030c, + EG2ReleaseTime = 0x030d, + EG2SustainLevel = 0x030e, + EG2DelayTime = 0x030f, + EG2HoldTime = 0x0310, + + // Filter Destinations + FilterCutoff = 0x0500, + FilterQ = 0x0501 +}; + +enum ConnTransformv1 : u16 { + None = 0x0000, + Concave = 0x0001, + Convex = 0x0002, + Switch = 0x0003 +}; + +bitfield ConnTransformv2 { + ConnTransformv1 outTransform : 4; + ConnTransformv1 ctrlTransform : 4; + bool ctrlBipolar : 1; + bool ctrlInvert : 1; + ConnTransformv1 srcTransform : 4; + bool srcBipolar : 1; + bool srcInvert : 1; +}; + +fn _u128guid(auto name, u32 timelow, u16 timemid, u16 timehighver, u8 clockseqrev, u8 clockseqlow, u8 node0, u8 node1, u8 node2, u8 node3, u8 node4, u8 node5) { + type::GUID result; + result.time_low = timelow; + result.time_mid = timemid; + result.time_high_and_version = timehighver; + result.clock_seq_high_and_reserved = clockseqrev; + result.clock_seq_low = clockseqlow; + result.node[0] = node0; + result.node[1] = node1; + result.node[2] = node2; + result.node[3] = node3; + result.node[4] = node4; + result.node[5] = node5; + return be u128(result); +}; + +struct ChunkHeader { + ChunkID chunkID; + u32 size; +} [[static]]; + +bitfield MIDIBank { + unsigned bankLSB : 7; + padding : 1; + unsigned bankMSB : 7; + padding : 16; + bool drum : 1; +}; + +bitfield MIDIInstrument { + unsigned instrument : 7; + padding : 25; +}; + +struct MIDILocale { + MIDIBank ulBank; + MIDIInstrument ulInstrument; +} [[static]]; + +struct RgnRange { + u16 usLow; + u16 usHigh; +} [[static]]; + +bitfield RgnhOptions { + SelfNonExclusive : 1; + padding : 15; +}; + +struct ConnectionBlockv1 { + ConnSource usSource; + ConnSource usControl; + ConnDestination usDestination; + ConnTransformv1 usTransform; + s32 lScale; +} [[static]]; + +struct ConnectionBlockv2 { + ConnSource usSource; + ConnSource usControl; + ConnDestination usDestination; + ConnTransformv2 usTransform; + s32 lScale; +} [[static]]; + +bitfield WlnkOptions { + bool PhaseMaster : 1; + bool Multichannel : 1; + padding : 14; +}; + +using WlnkChannel = type::WlnkChannel; // provided by type.mmreg + +bitfield WsmpOptions { + NoTruncation : 1; + NoCompression : 1; + padding : 30; +}; + +enum LoopType : u32 { + Forward = 0, + Release = 1 // Loop and Release +}; + +struct WavesampleLoop { + u32 cbSize; + LoopType ulLoopType; + u32 ulLoopStart; + u32 ulLoopLength; + // padding[cbSize-($-addressof(cbSize))]; +} [[static]]; + +using TypewFormatTag = type::TypewFormatTag; // provided by type.mmreg + +struct WAVEFORMAT { + type::TypewFormatTag wfmt = std::mem::read_unsigned($, 2, std::core::get_endian()); + + if (wfmt == TypewFormatTag::WAVE_FORMAT_PCM) { + type::PCMWAVEFORMAT fmt; + } else if (wfmt == TypewFormatTag::WAVE_FORMAT_ALAW || wfmt == TypewFormatTag::WAVE_FORMAT_MULAW) { + type::WAVEFORMAT fmt; + } else if (wfmt == TypewFormatTag::WAVE_FORMAT_EXTENSIBLE || wfmt == TypewFormatTag::WAVE_FORMAT_IEEE_FLOAT) { + type::WAVEFORMATEXTENSIBLE fmt; + } else if (wfmt == TypewFormatTag::WAVE_FORMAT_ADPCM) { + type::ADPCMWAVEFORMAT fmt; + } else if (wfmt == TypewFormatTag::WAVE_FORMAT_IMA_ADPCM) { + type::IMAADPCMWAVEFORMAT fmt; + } else if (wfmt == TypewFormatTag::WAVE_FORMAT_MPEG) { + type::MPEG1WAVEFORMAT fmt; + } else if (wfmt == TypewFormatTag::WAVE_FORMAT_MPEGLAYER3) { + type::MPEGLAYER3WAVEFORMAT fmt; + } else { + type::WAVEFORMAT fmt; + std::warning(std::format("Unsupported wFormatTag: 0x{:X}", u16(wfmt))); + } +}; + +enum CdlOpcode : u16 { + And = 0x0001, + Or = 0x0002, + Xor = 0x0003, + Add = 0x0004, + Subtract = 0x0005, + Multiply = 0x0006, + Divide = 0x0007, + LogicalAnd = 0x0008, + LogicalOr = 0x0009, + LessThan = 0x000A, + LessEqual = 0x000B, + GreaterThan = 0x000C, + GreaterEqual = 0x000D, + Equal = 0x000E, + Not = 0x000F, + Const = 0x0010, + Query = 0x0011, + QuerySupported = 0x0012 +}; + +enum CdlDLSID : u128 { + GMInHardware = "DLSID_GMInHardware"_u128guid(0x178f2f24, 0xc364, 0x11d1, 0xa7, 0x60, 0x00, 0x00, 0xf8, 0x75, 0xac, 0x12), + GSInHardware = "DLSID_GSInHardware"_u128guid(0x178f2f25, 0xc364, 0x11d1, 0xa7, 0x60, 0x00, 0x00, 0xf8, 0x75, 0xac, 0x12), + XGInHardware = "DLSID_XGInHardware"_u128guid(0x178f2f26, 0xc364, 0x11d1, 0xa7, 0x60, 0x00, 0x00, 0xf8, 0x75, 0xac, 0x12), + SupportsDLS1 = "DLSID_SupportsDLS1"_u128guid(0x178f2f27, 0xc364, 0x11d1, 0xa7, 0x60, 0x00, 0x00, 0xf8, 0x75, 0xac, 0x12), + SupportsDLS2 = "DLSID_SupportsDLS2"_u128guid(0xf14599e5, 0x4689, 0x11d2, 0xaf, 0xa6, 0x0, 0xaa, 0x0, 0x24, 0xd8, 0xb6), + SampleMemorySize = "DLSID_SampleMemorySize"_u128guid(0x178f2f28, 0xc364, 0x11d1, 0xa7, 0x60, 0x00, 0x00, 0xf8, 0x75, 0xac, 0x12), + ManufacturersID = "DLSID_ManufacturersID"_u128guid(0xb03e1181, 0x8095, 0x11d2, 0xa1, 0xef, 0x0, 0x60, 0x8, 0x33, 0xdb, 0xd8), + ProductID = "DLSID_ProductID"_u128guid(0xb03e1182, 0x8095, 0x11d2, 0xa1, 0xef, 0x0, 0x60, 0x8, 0x33, 0xdb, 0xd8), + SamplePlaybackRate = "DLSID_SamplePlaybackRate"_u128guid(0x2a91f713, 0xa4bf, 0x11d2, 0xbb, 0xdf, 0x0, 0x60, 0x8, 0x33, 0xdb, 0xd8) +}; + +struct CdlOperation { + CdlOpcode opcode; + if (opcode == CdlOpcode::Const) { + u32 constant; + } else if (opcode == CdlOpcode::Query || opcode == CdlOpcode::QuerySupported) { + CdlDLSID query; + } +}; + +// "cue " just happened to exist in Samsung's DLSs +struct WaveCuePoint { + u32 indentifier; + u32 position; + ChunkID chunkID; + u32 chunkStart; + u32 blockStart; + u32 sampleOffset; +} [[static]]; + +using Chunk; + +struct Chunk { + ChunkHeader header; + u64 endaddr = addressof(header)+sizeof(header)+header.size; // past-the-end address + if (header.chunkID == ChunkID::RIFF) { + ChunkID riffType; + Chunk chunks[while(endaddr-$ > 1)]; + } else if (header.chunkID == ChunkID::LIST) { + ChunkID listType; + if (!std::core::is_valid_enum(listType)) { + char fmt[4] @ addressof(listType) [[hidden]]; + std::warning(std::format("Unknown LIST type: 0x{:X} \"{}\"", u32(listType), fmt)); + } + Chunk data[while(endaddr-$ > 1)]; + } else if (u32(header.chunkID) & 0xFF == 0x49) { // begin with I + char zstr[]; + } else if (header.chunkID == ChunkID::colh) { + u32 cInstruments; + } else if (header.chunkID == ChunkID::dlid) { + type::GUID dlsid; + } else if (header.chunkID == ChunkID::insh) { + u32 cRegions; + MIDILocale locale; + } else if (header.chunkID == ChunkID::rgnh) { + RgnRange rangeKey; + RgnRange rangeVelocity; + RgnhOptions fusOptions; + u16 usKeyGroup; + } else if (header.chunkID == ChunkID::art1) { + u32 cbSize; + u32 cConnectionBlocks; + padding[cbSize-($-addressof(cbSize))]; + ConnectionBlockv1 connectionBlocks[cConnectionBlocks]; + } else if (header.chunkID == ChunkID::art2) { + u32 cbSize; + u32 cConnectionBlocks; + padding[cbSize-($-addressof(cbSize))]; + ConnectionBlockv2 connectionBlocks[cConnectionBlocks]; + } else if (header.chunkID == ChunkID::wlnk) { + WlnkOptions fusOptions; + u16 usPhaseGroup; + WlnkChannel ulChannel; + u32 ulTableIndex; + } else if (header.chunkID == ChunkID::wsmp) { + u32 cbSize; + u16 usUnityNote; + s16 sFineTune; + s32 lAttenuation; + WsmpOptions fulOptions; + u32 cSampleLoops; + padding[cbSize-($-addressof(cbSize))]; + WavesampleLoop sampleLoops[cSampleLoops]; + } else if (header.chunkID == ChunkID::ptbl) { + u32 cbSize; + u32 cCues; + padding[cbSize-($-addressof(cbSize))]; + u32 poolcues[cCues]; + } else if (header.chunkID == ChunkID::vers) { + u32 dwVersionMS; + u32 dwVersionLS; + } else if (header.chunkID == ChunkID::fmt) { + WAVEFORMAT format; + } else if (header.chunkID == ChunkID::data) { + u8 data[header.size]; + } else if (header.chunkID == ChunkID::cdl) { + CdlOperation operations[while(endaddr-$ > 1)]; + } else if (header.chunkID == ChunkID::cue) { + u32 nCuePoints; + WaveCuePoint cuePoints[nCuePoints]; + } else if (header.chunkID == ChunkID::labl || header.chunkID == ChunkID::note) { + u32 cuepointID; + char zstr[]; + } else if (header.chunkID == ChunkID::fact) { + u32 dwSamples; + } else { + char fmt[4] @ addressof(header.chunkID) [[hidden]]; + std::warning(std::format("Unknown chunk ID: 0x{:X} \"{}\"", u32(header.chunkID), fmt)); + } + try { + padding[header.size-($-(addressof(header)+sizeof(header)))]; + } catch { + std::warning(std::format("Failed to place chunk: computed size {} bigger than actual {}", $-(addressof(header)+sizeof(header)), header.size)); + std::error("Failed to place chunk"); + } + if ($ % 2 != 0) { + padding[1]; + } +}; + +Chunk riff @ 0x00; \ No newline at end of file diff --git a/patterns/wav.hexpat b/patterns/wav.hexpat index e8595726..c3c3c7c2 100644 --- a/patterns/wav.hexpat +++ b/patterns/wav.hexpat @@ -3,7 +3,9 @@ #pragma MIME audio/x-wav #pragma MIME audio/wav +import std.core; import std.mem; +import type.mmreg; struct RiffHeader { char ckID[4] [[comment("Container Signature"), name("RIFF Header Signature")]]; @@ -16,106 +18,6 @@ struct WaveChunk { 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; }; @@ -196,27 +98,23 @@ struct WavData { paddedChunkSize = (chunk.chunkSize + 1) >> 1 << 1; if (chunk.chunkId == "fmt ") { - WaveFormat fmt; + u128 start = $; + type::TypewFormatTag wfmt = std::mem::read_unsigned($, 2, std::core::get_endian()); - if (fmt.formatTag == WaveFormatType::PCM) { - WaveFormatPCM pcmExtraData; - padding[paddedChunkSize - sizeof(fmt) - sizeof(pcmExtraData)]; + if (wfmt == WaveFormatType::PCM) { + type::PCMWAVEFORMAT waveformat; } else if (fmt.formatTag == WaveFormatType::MS_ADPCM) { - WaveFormatMSADPCM msAdpcmExtraData; - padding[paddedChunkSize - sizeof(fmt) - sizeof(msAdpcmExtraData)]; + type::ADPCMWAVEFORMAT waveformat; } else if (fmt.formatTag == WaveFormatType::MPEG) { - WaveFormatMPEG mpegExtraData; - padding[paddedChunkSize - sizeof(fmt) - sizeof(mpegExtraData)]; + type::MPEG1WAVEFORMAT waveformat; } else if (fmt.formatTag == WaveFormatType::MPEGLAYER3) { - WaveFormatMPEGLayer3 mpegLayer3ExtraData; - padding[paddedChunkSize - sizeof(fmt) - sizeof(mpegLayer3ExtraData)]; + type::MPEGLAYER3WAVEFORMAT waveformat; } else if (fmt.formatTag == WaveFormatType::IEEEFloatingPoint) { - WaveFormatIEEEFloatingPoint ieeFloatingPointExtraData; - padding[paddedChunkSize - sizeof(fmt) - sizeof(ieeFloatingPointExtraData)]; + type::WAVEFORMATIEEEFLOATEX waveformat; } else { - WaveFormatExDummy unknown; - padding[paddedChunkSize - sizeof(fmt) - sizeof(unknown)]; + type::WAVEFORMAT waveformat_unknown; } + padding[paddedChunkSize - ($ - start)]; } else if (chunk.chunkId == "data") { padding[paddedChunkSize]; } else if (chunk.chunkId == "fact") { diff --git a/patterns/xmf.hexpat b/patterns/xmf.hexpat new file mode 100644 index 00000000..72a9c2c0 --- /dev/null +++ b/patterns/xmf.hexpat @@ -0,0 +1,589 @@ +#pragma description MMA XMF and MXMF + +// This implements: +// RP-030 Specification for XMF Meta File Format 1.0 +// RP-031 Type 0 & Type 1 XMF Files (DLS + SMF) +// RP-032 SMF Meta Event for XMF Patch Type Prefix +// RP-039 XMF Meta File Format Updates 1.01 +// RP-040 XMF Compressions Definition for “zlib” +// RP-042a Type 2 XMF File (Mobile XMF) +// RP-043 Specification for XMF Meta File Format 2.0 +// RP-045 Audio Clips for Mobile XMF +// RP-047 ID3 Metadata for XMF Files + +#pragma endian big + +// audio/xmfN does not seem to be used +#pragma MIME audio/mobile-xmf + +import std.core; +import std.io; +import std.mem; +import std.array; +import std.attrs; +import std.string; +import type.guid; +import hex.dec; +import hex.core; +import type.mmreg; + +import * from id3 as ID3; +import * from midi as SMF; +import * from dls as DLS; + +// As referred in RP-039, ReferenceTypeID 2 InfileResource does not provide any indication of the length of the target data +// So we just make it extend to EOF +// If you dont like that, set this to false +bool extendIFRtoEOF in; + +fn u32ify(u32 vlq) { + // Converts from variable-length quantity to u32. These numbers are + // represented 7 bits per byte, most significant bits first. All bytes + // except the last have bit 7 set, and the last byte has bit 7 clear. + // If the number is in range 0..127, it is thus represented exactly + // as one byte. + u32 n = vlq & 0x7f; + if (vlq & 0x8000 == 0x8000) { + n += ((vlq & 0x7f00) >> 8) * 0x80; + } + if (vlq & 0x800000 == 0x800000) { + n += ((vlq & 0x7f0000) >> 8 * 2) * 0x80 * 0x80; + } + if (vlq & 0x80000000 == 0x80000000) { + n += ((vlq & 0x7f000000) >> 8 * 3) * 0x80 * 0x80 * 0x80; + } + return n; +}; + +fn unwrapVLQ(auto vlq) { + return u32ify(vlq.value); +}; + +// TODO: We got VLQs up to 19 bytes long for non-registered ids, and how can we handle it... +struct VLQ : std::attr::Literal<"unwrapVLQ"> { + if (std::mem::read_unsigned($, 1) & 0x80 == 0x80) { + if (std::mem::read_unsigned($ + 1, 1) & 0x80 == 0x80) { + if (std::mem::read_unsigned($ + 2, 1) & 0x80 == 0x80) { + u32 value; + } else { + u24 value; + } + } else { + u16 value; + } + } else { + u8 value; + } +}; + +struct VLQPointer { + VLQ addr; + T deref @ addr; +}; + +// make it extend to EOF +struct VLQUnsizedPointer { + VLQ addr; + if (extendIFRtoEOF) { + char deref[while(!std::mem::eof())] @ addr; + } +}; + +struct VLQPointerToArray { + VLQ addr; + T deref[size] @ addr; +}; + +using MetaDataStandardResourceFormatID; + +// TODO: `ref` seems nonsense, but without it resfmt will not be passed correctly +fn inferResourceSize(u64 addr, ref MetaDataStandardResourceFormatID resfmt) { + // std::print("2. 0x{:x} {} {} 0x{:x}", $, resfmt, u8(resfmt), addressof(resfmt)); + u64 filesize = 0; + // std::print("3. 0x{:x} {} {} 0x{:x}", $, resfmt, u8(resfmt), addressof(resfmt)); + if (resfmt == MetaDataStandardResourceFormatID::SMF0 + || resfmt == MetaDataStandardResourceFormatID::SMF1) { + if (std::mem::read_string(addr, 4) != "MThd") { + std::warning("A SMF resource does not seem to be a SMF file"); + std::error(); + } + filesize += 8 + std::mem::read_unsigned(addr+4, 4, std::mem::Endian::Big); + u16 ntrks = std::mem::read_unsigned(addr+10, 2, std::mem::Endian::Big); + for (u16 i = 0, i < ntrks, i += 1) { + if (std::mem::read_string(addr+filesize, 4) != "MTrk") { + std::warning("A SMF resource does not seem to be a SMF file (when reading its tracks)"); + std::error(); + } + filesize += 8 + std::mem::read_unsigned(addr+filesize+4, 4, std::mem::Endian::Big); + } + } else if (std::core::is_valid_enum(resfmt)) { // If it is not SMF, it is DLS + if (std::mem::read_string(addr, 4) != "RIFF") { + std::warning("A DLS resource does not seem to be a RIFF file"); + std::error(); + } + filesize = 4 + std::mem::read_unsigned(addr+4, 4, std::mem::Endian::Little); + } else { + std::warning("Invalid MetaDataStandardResourceFormatID found"); + std::error(); + } + return filesize; +}; + +// Enums + +enum FileID : u32 { + XMF_ = 0x584D465F // "XMF_" +}; + +enum ReferenceTypeID : u8 { + InlineResource = 0x01, + InFileResource = 0x02, + InFileNode = 0x03, + ExternalResourceFile = 0x04, + XMFURI = 0x05, + XMFURIAndNodeID = 0x06 +}; + +enum XMFMetaFileVersion : u32 { + v1 = 0x312E3030, + v2 = 0x322E3030 +}; + +enum MetaDataStandardFieldID : u8 { // actually VLQ + XMFFileType = 0, + NodeName = 1, + NodeID = 2, + ResourceFormat = 3, + FilenameOnDisk = 4, + FilenameExtensionOnDisk = 5, + MacOSFileType = 6, + MIMEType = 7, + Title = 8, + Copyright = 9, + Comment = 10, + Autostart = 11, + Preload = 12, + ContentDescription = 13, + ID3Metadata = 14 +}; + +enum MetaDataStandardResourceFormatID : u8 { // actually VLQ + SMF0 = 0, + SMF1 = 1, + DLS1 = 2, + DLS2 = 3, + DLS21 = 4, + MDLS = 5 +}; + +enum StandardTypeID : u8 { // actually VLQ + Standard = 0, + MMAManufacturer = 1, + Registered = 2, + Nonregistered = 3 +}; + +enum PRLTypeID : u8 { + Standard = 0, + MMAManufacturer = 1, + Registered = 2, + Nonregistered = 3, + WTCodecFormatTag = 4, + WTCodecGUID = 5 +}; + +enum PRGLGroupID : u8 { + SynthesizerVoice = 0, + WTCodec = 1, + WTMemoryConsumption = 2, + // exclusive means it cuts itself + AudioClipsExclusive = 3, + AudioClipsNonexclusive = 4, + AudioClipsVoice = 5 +}; + +enum PRLStandardID : u8 { + // PRGL 0 + GM1VoiceCount = 0, + MDLSVoiceCount = 1, + MDLSVoiceCountWithControlGroup = 2, + + // PRGL 2 + DLSWTSizeKiB = 3, // Total wavetable data consumption (in kilobytes) for Mobile DLS instruments using (16-bit, 8-bit) PCM samples + MDLSWTSizeKiB = 4, // Total uncompressed wavetable data (in kilobytes) for Mobile DLS instruments using compressed samples + + // PRGL 5 + AudioClipsCount = 5, + + // PRGL 3 + AudioClipsWTSizeKiB = 6, + + // PRGL 4 + AudioClipsMaxSR = 7, // in kilosamples per 10 seconds + + // PRGL 3 + // in kilobits per 10 seconds + AudioClipsAverageBitrate = 8, + AudioClipsMaxBitrate = 9 +}; + +enum StringFormatTypeID : u8 { // actually VLQ + EASCIIVisible = 0, + EASCIIHidden = 1, + UTF16Visible = 2, + UTF16Hidden = 3, + SCSUVisible = 4, + SCSUHidden = 5, + BinaryVisible = 6, + BinaryHidden = 7 +}; + +enum StandardUnpackerID : u8 { // actually VLQ + None = 0, + ZLIB = 1 +}; + +// Atomic + +struct XString { + VLQ length; + char data[(length)]; +}; + +// Metadata + +struct PRLEntry { + PRLTypeID typeID; + if (typeID == PRLTypeID::Standard) { + PRLStandardID standardID; + } else if (typeID == PRLTypeID::MMAManufacturer) { + VLQ manufacturerID; + VLQ internalID; + } else if (typeID == PRLTypeID::Registered) { + VLQ registeredID; + } else if (typeID == PRLTypeID::Nonregistered) { + type::GUID guid; + } else if (typeID == PRLTypeID::WTCodecFormatTag) { + type::TypewFormatTag wFormatTag; + } else if (typeID == PRLTypeID::WTCodecGUID) { + type::GUID guid; + } +}; + +// MXMF +struct MIRCountTableChannel { + VLQ MIRCount[parent.PlaybackResourceCount]; +}; + +struct ContentDescription { + VLQ MIPIndex; + VLQ ChannelCount; + VLQ PlaybackResourceCount; + PRLEntry PRL[PlaybackResourceCount]; + PRGLGroupID PRGL[PlaybackResourceCount]; + MIRCountTableChannel MIRCountTable[ChannelCount]; +}; + +struct MetaDataTypeEntry { + VLQ metadataType; + VLQ stringFormatTypeID; + XString langCountrySpec; +}; + +struct MetaDataTypesTable { + VLQ size; + if (size != 0) { + VLQ numberOfEntries; + MetaDataTypeEntry typeEntries[numberOfEntries]; + } +}; + +struct FieldSpecifier { + if (std::mem::read_unsigned($, 1) == 0) { + u8; + // VLQ fieldID; + MetaDataStandardFieldID fieldID; // standard fieldid is no longer than u8 + } else { + XString fieldName; + } +}; + +struct ContentVersion { + VLQ metadataType; + VLQ lengthOfData; + char versionData[lengthOfData]; +}; + +struct FieldContents { + VLQ numberOfVersions; + VLQ lengthOfData; + if (lengthOfData == 0) { + u8; // stirngFormatTypeID + } else { + if (numberOfVersions == 0) { + StringFormatTypeID stringFormatTypeID; + + if (parent.fieldSpecifier.fieldID == MetaDataStandardFieldID::XMFFileType) { + VLQ fileTypeID; + VLQ revisionID; + } else if (parent.fieldSpecifier.fieldID == MetaDataStandardFieldID::NodeID) { + VLQ nodeID; + } else if (parent.fieldSpecifier.fieldID == MetaDataStandardFieldID::ResourceFormat) { + StandardTypeID formatTypeID; + if (formatTypeID == StandardTypeID::Standard) { + MetaDataStandardResourceFormatID standardResourceFormatID; + } else { + VLQ resourceFormatID; + } + } else if (parent.fieldSpecifier.fieldID == MetaDataStandardFieldID::ContentDescription) { + ContentDescription contentDescription; + } else if (parent.fieldSpecifier.fieldID == MetaDataStandardFieldID::ID3Metadata) { + ID3 id3v2; + } else { + char universalData[lengthOfData-sizeof(stringFormatTypeID)]; + } + padding[addressof(stringFormatTypeID)+lengthOfData-$]; + } else { + ContentVersion versions[while(!std::mem::reached(addressof(versions)+size))]; + } + } +}; + +struct MetaDataItem { + FieldSpecifier fieldSpecifier; + FieldContents fieldContents; +}; + +struct NodeMetaData { + VLQ size; + MetaDataItem items[while(!std::mem::reached(addressof(this)+size))]; +}; + +// Unpacker + +struct UnpackerID { + StandardTypeID typeID; + if (typeID == StandardTypeID::Standard) { + StandardUnpackerID standardUnpackerID; + } else { + VLQ id; + } +}; + +struct UnpackerEntry { + UnpackerID unpackerID; + VLQ decodedLength; +}; + +struct NodeUnpackers { + VLQ size; + if (size != 0) { + UnpackerEntry unpackerEntries[while(!std::mem::reached(addressof(this)+size))]; + } +}; + +fn unpackData(auto unpackerEntries, auto pattern, std::mem::Section dest) { + bool unpacked = false; + std::mem::copy_value_to_section(pattern, dest, 0); + for (u32 i = 0, i < std::core::member_count(unpackerEntries), i += 1) { + if (unpackerEntries[i].unpackerID.typeID == StandardTypeID::Standard) { + if (unpackerEntries[i].unpackerID.standardUnpackerID == StandardUnpackerID::None) { + continue; + } else if (unpackerEntries[i].unpackerID.standardUnpackerID == StandardUnpackerID::ZLIB) { + u8 src[std::mem::get_section_size(dest)] @ 0x00 in dest; + if (!hex::dec::zlib_decompress(src, dest, 0)) { + std::error("Failed to decompress ZLIB data"); + } + unpacked = true; + } else { + std::error("Unrecognized StandardUnpackerID"); + } + } else { + std::error("Unsupported UnpackerID"); + } + } + return unpacked; +}; + +// Node + +using Node; +using NodeWithPadding; + +struct NodeHeader { + VLQ nodeSize; + VLQ itemCount; + VLQ headerSize; + NodeMetaData nodeMetadata; + NodeUnpackers nodeUnpackers; +}; + +fn getResourceFormatIncomplete(ref NodeHeader header, ReferenceTypeID typeID, auto pointerToFileNode) { + if (header.itemCount != 0) { + std::error("Cannot fetch resource format from a folder node"); + } + if (typeID == ReferenceTypeID::InFileNode) { + return getResourceFormat(pointerToFileNode.deref); + } + for (u32 i = 0, i < header.nodeMetadata.size, i += 1) { + if (header.nodeMetadata.items[i].fieldSpecifier.fieldID == MetaDataStandardFieldID::ResourceFormat) { + if (header.nodeMetadata.items[i].fieldContents.formatTypeID == StandardTypeID::Standard) { + return header.nodeMetadata.items[i].fieldContents.standardResourceFormatID; + } + return header.nodeMetadata.items[i].fieldContents.resourceFormatID; + } + } +}; + +fn getResourceFormat(ref Node node) { + if (node.content.typeID == ReferenceTypeID::InFileNode) { + return getResourceFormat(node.header, node.content.typeID, node.content.pointerToFileNode); + } + return getResourceFormat(node.header, node.content.typeID, 0); +}; + +struct NodeContents { + ReferenceTypeID typeID; + if (parent.header.itemCount > 0) { + if (typeID == ReferenceTypeID::InlineResource) { + NodeWithPadding children[parent.header.itemCount] [[inline]]; + } else if (typeID == ReferenceTypeID::InFileResource) { + VLQPointerToArray pointerToChildren; + } else if (typeID == ReferenceTypeID::InFileNode) { + VLQPointer pointerToFolderNode; + } else { + std::error("Unrecognized or invalid ReferenceTypeID for FolderNode"); + } + } else { + if (typeID == ReferenceTypeID::InlineResource) { + char data[parent.header.nodeSize-($-addressof(parent))]; + if (parent.header.nodeUnpackers.size != 0) { + try { + std::mem::Section unpacked = std::mem::create_section(std::format("Unpacked 0x{:x}", addressof(data))); + if (unpackData(parent.header.nodeUnpackers.unpackerEntries, data, unpacked)) { + std::print("Data at 0x{:x} in {} bytes decompressed in {} bytes (see Sections)", addressof(data), sizeof(data), std::mem::get_section_size(unpacked)); + char unpackedData[std::mem::get_section_size(unpacked)] @ 0x00 in unpacked; + } + } catch { + std::warning("Something unexpected happened while unpacking data at 0x{:x}", addressof(data)); + } + } + } else if (typeID == ReferenceTypeID::InFileResource) { + VLQ ptr [[hidden, no_unique_address]]; + try { + MetaDataStandardResourceFormatID resfmt = getResourceFormatIncomplete(parent.header, typeID, 0); + // std::print("1. 0x{:x} {} {} 0x{:x}", $, resfmt, u8(resfmt), addressof(resfmt)); + VLQPointerToArray pointerToData; + } catch { + std::warning(std::format("Failed to deduce size of InFileResource at 0x{:x} pointing to 0x{:x}", addressof(this), ptr)); + if (extendIFRtoEOF) { + std::warning("The resource will be extended to the EOF."); + } else { + std::warning("The resource will not be patterned. You can let it extend to EOF by setting extendIFRtoEOF to true in the 'Settings' tab above."); + } + VLQUnsizedPointer pointerToData; + } + } else if (typeID == ReferenceTypeID::InFileNode) { + VLQPointer pointerToFileNode; + } else if (typeID == ReferenceTypeID::ExternalResourceFile || typeID == ReferenceTypeID::XMFURI) { + XString externalURI; + } else if (typeID == ReferenceTypeID::XMFURIAndNodeID) { + XString uri; // nullable + VLQ nodeID; + } else { + std::error("Unrecognized ReferenceTypeID"); + } + } +}; + +struct Node { + NodeHeader header; + padding[header.headerSize-sizeof(header)]; + NodeContents content; +}; + +struct NodeWithPadding { + Node node; + padding[node.header.nodeSize-sizeof(node)]; +}; + +// File + +struct FileHeader { + FileID fileID; + XMFMetaFileVersion fileVer; + if (fileVer == XMFMetaFileVersion::v2) { + u32 fileTypeID; + u32 fileTypeRevisionID; + } + VLQ fileSize; + MetaDataTypesTable metadataTypesTable; + VLQ treeStart; + VLQ treeEnd; +}; + +fn fetchChild(ref Node tree, u32 idx) { + if (tree.header.itemCount == 0) { + std::error("Cannot fetch child from a file node"); + } + if (tree.content.typeID == ReferenceTypeID::InlineResource) { + return tree.content.children[idx].node; + } + if (tree.content.typeID == ReferenceTypeID::InFileResource) { + return tree.content.pointerToChildren[idx].node; + } + if (tree.content.typeID == ReferenceTypeID::InFileNode) { + return fetchChild(tree.content.pointerToFolderNode.deref, idx); + } + std::error("Unrecognized or invalid ReferenceTypeID for FolderNode"); +}; + +fn fetchData(ref Node node) { + if (node.header.itemCount != 0) { + std::error("Cannot fetch data from a folder node"); + } + if (node.content.typeID == ReferenceTypeID::InlineResource) { + try { + return node.content.unpackedData; + } + u8 pattern[sizeof(node.content.data)] @ addressof(node.content.data) in 0; + return pattern; + } + if (node.content.typeID == ReferenceTypeID::InFileResource) { + try { + u8 pattern[sizeof(node.content.pointerToData.deref)] @ addressof(node.content.pointerToData.deref) in 0; + return pattern; + } catch { + std::mem::Section errsec = std::mem::create_section(""); + std::mem::copy_value_to_section(std::format("Failed to deref InFileResource at 0x{:x} pointing to 0x{:x}", addressof(node.content), node.content.pointerToData.addr), errsec, 0); + u8 errmsg[std::mem::get_section_size(errsec)] @ 0x00 in errsec; + return errmsg; + } + } + if (node.content.typeID == ReferenceTypeID::InFileNode) { + return fetchData(node.content.pointerToFileNode.deref); + } + std::error("Unrecognized or unsupported ReferenceTypeID {}", node.content.typeID); +}; + +fn formatNodeName(ref Node node) { + try { + return std::format("{:X}h.{}", addressof(node), std::string::replace(std::core::formatted_value(getResourceFormat(node)), "MetaDataStandardResourceFormatID::", "")); + } + return std::format("{:X}h", addressof(node)); +}; + +fn visualizeTree(str prefix, ref Node tree) { + if (tree.header.itemCount == 0) { + hex::core::add_virtual_file(std::format("{}{}", prefix, formatNodeName(tree)), fetchData(tree)); + } else { + for (u32 i = 0, i < tree.header.itemCount, i += 1) { + visualizeTree(std::format("{}{:X}h/", prefix, addressof(tree)), fetchChild(tree, i)); + } + } +}; + +FileHeader fileHeader @ 0x00; +Node tree @ fileHeader.treeStart; + +visualizeTree("/", tree); \ No newline at end of file diff --git a/tests/patterns/test_data/dls.hexpat.dls b/tests/patterns/test_data/dls.hexpat.dls new file mode 100644 index 00000000..4526febc Binary files /dev/null and b/tests/patterns/test_data/dls.hexpat.dls differ diff --git a/tests/patterns/test_data/xmf.hexpat.mxmf b/tests/patterns/test_data/xmf.hexpat.mxmf new file mode 100644 index 00000000..239f5009 Binary files /dev/null and b/tests/patterns/test_data/xmf.hexpat.mxmf differ