Last active
November 21, 2024 15:44
-
-
Save YuriSizov/e268372aa30aa7aeb279cc7a5809b250 to your computer and use it in GitHub Desktop.
XM patterns for ImHex
This file contains 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 YuriSizov | |
#pragma description FastTracker 2 XM File | |
#pragma magic [ 45 78 74 65 6E 64 65 64 20 4D 6F 64 75 6C 65 3A 20 ] @ 0x00 | |
#include "std/core.pat" | |
#include "std/string.pat" | |
// HEADERS. | |
struct XMHeader { | |
char module_head[0x11]; | |
char module_name[0x14]; | |
padding[1]; | |
char tracker_name[0x14]; | |
u8 version_min; | |
u8 version_maj; | |
}; | |
struct SongHeader { | |
u32 header_size; | |
u16 song_length; | |
u16 restart_pos; | |
u16 channel_num; | |
u16 pattern_num; | |
u16 instrument_num; | |
u16 song_flags; | |
u16 default_tempo; | |
u16 default_bpm; | |
u8 pattern_order_table[song_length]; | |
padding[0x100 - song_length]; | |
}; | |
// PATTERNS. | |
bitfield PackedPatternFlags { | |
bool note_follows : 1; | |
bool instrument_follows : 1; | |
bool volume_follows : 1; | |
bool effect_type_follows : 1; | |
bool effect_value_follows : 1; | |
padding : 2; | |
bool packed : 1; | |
}; | |
struct PatternEnvelope { | |
if ((std::mem::read_unsigned($, 1) >> 7) & 1) { | |
PackedPatternFlags flags; | |
if (flags.note_follows) { | |
u8 note; | |
} else { | |
u8 note = 0; | |
} | |
if (flags.instrument_follows) { | |
u8 instrument; | |
} else { | |
u8 instrument = 0; | |
} | |
if (flags.volume_follows) { | |
u8 volume; | |
} else { | |
u8 volume = 0; | |
} | |
if (flags.effect_type_follows) { | |
u8 effect_type; | |
} else { | |
u8 effect_type = 0; | |
} | |
if (flags.effect_value_follows) { | |
u8 effect_value; | |
} else { | |
u8 effect_value = 0; | |
} | |
} else { | |
u8 note; | |
u8 instrument; | |
u8 volume; | |
u8 effect_type; | |
u8 effect_value; | |
} | |
}; | |
fn format_pattern_envelope(ref PatternEnvelope p) { | |
return std::format("{:02}:{} ({}|{}|{})", p.note, p.instrument, p.volume, p.effect_type, p.effect_value); | |
}; | |
struct PatternRow { | |
PatternEnvelope envelopes[song_header.channel_num] [[inline, format_entries("format_pattern_envelope")]]; | |
}; | |
fn format_pattern_row(ref PatternRow r) { | |
str output = ""; | |
for (u8 i = 0, i < song_header.channel_num, i = i + 1) { | |
output += format_pattern_envelope(r.envelopes[i]) + " "; | |
} | |
return output; | |
}; | |
union PatternData { | |
PatternRow rows[parent.row_num] [[format_entries("format_pattern_row")]]; | |
u8 raw_data[parent.data_size]; | |
}; | |
struct PatternSection { | |
u32 header_size; | |
padding[1]; | |
u16 row_num; | |
u16 data_size; | |
u32 array_end_addr = $ + data_size; | |
PatternData data [[inline]]; | |
}; | |
// INSTRUMENTS. | |
struct InstrumentHeader { | |
u32 header_size; | |
char instrument_name[0x16]; | |
u8 instrument_type; | |
u16 sample_num; | |
}; | |
struct InstrumentEnvelopePoint { | |
u16 x, y; | |
}; | |
fn format_instrument_env_point(ref InstrumentEnvelopePoint p) { | |
return std::format("[ {}, {} ]", p.x, p.y); | |
}; | |
struct InstrumentExtraHeader { | |
u32 sample_header_size; | |
u8 note_numbers[0x60]; | |
InstrumentEnvelopePoint volume_points[0x0C] [[format_entries("format_instrument_env_point")]]; | |
InstrumentEnvelopePoint panning_points[0x0C] [[format_entries("format_instrument_env_point")]]; | |
u8 number_of_volume_points; | |
u8 number_of_panning_points; | |
u8 volume_sustain_point; | |
u8 volume_loop_start_point; | |
u8 volume_loop_end_point; | |
u8 panning_sustain_point; | |
u8 panning_loop_start_point; | |
u8 panning_loop_end_point; | |
u8 volume_type; | |
u8 panning_type; | |
u8 vibrato_type; | |
u8 vibrato_sweep; | |
u8 vibrato_depth; | |
u8 vibrato_rate; | |
u16 volume_fadeout; | |
}; | |
struct SampleHeader { | |
u32 sample_length; | |
u32 loop_start; | |
u32 loop_length; | |
u8 volume; | |
u8 finetune; | |
u8 flags; | |
u8 panning; | |
u8 relative_note; | |
padding[1]; | |
char sample_name[0x16]; | |
}; | |
struct SampleData { | |
u8 data[parent.sample_header[std::core::array_index()].sample_length]; | |
}; | |
struct InstrumentSection { | |
InstrumentHeader header; | |
u32 extra_header_size = header.header_size - sizeof(header); | |
if (header.sample_num > 0) { | |
InstrumentExtraHeader extra_header; | |
padding[extra_header_size - sizeof(extra_header)]; | |
SampleHeader sample_header[header.sample_num]; | |
SampleData sample_data[header.sample_num]; | |
} | |
}; | |
// FILE LAYOUT. | |
XMHeader xm_header @ 0x00; | |
SongHeader song_header @ 0x3C; | |
PatternSection xm_patterns[song_header.pattern_num] @ $; | |
InstrumentSection xm_instruments[song_header.instrument_num] @ $; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment