Skip to content

Instantly share code, notes, and snippets.

@YuriSizov
Last active November 21, 2024 15:44
Show Gist options
  • Save YuriSizov/e268372aa30aa7aeb279cc7a5809b250 to your computer and use it in GitHub Desktop.
Save YuriSizov/e268372aa30aa7aeb279cc7a5809b250 to your computer and use it in GitHub Desktop.
XM patterns for ImHex
#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