99 lines
2.5 KiB
C++
99 lines
2.5 KiB
C++
#include "mod.hpp"
|
|
#include "assert.h"
|
|
|
|
static const uint8_t * reader_s(struct reader * r, int length)
|
|
{
|
|
const uint8_t * ptr = &r->buf[r->ix];
|
|
r->ix += length;
|
|
return ptr;
|
|
}
|
|
|
|
static void reader_bytes(struct reader * r, int length, uint8_t * dst)
|
|
{
|
|
const uint8_t * ptr = reader_s(r, length);
|
|
for (int i = 0; i < length; i++)
|
|
dst[i] = ptr[i];
|
|
}
|
|
|
|
static int reader_u8(struct reader * r)
|
|
{
|
|
const uint8_t * ptr = reader_s(r, 1);
|
|
return *ptr;
|
|
}
|
|
|
|
static int reader_u16(struct reader * r)
|
|
{
|
|
const uint8_t * ptr = reader_s(r, 2);
|
|
int value = (((uint32_t)ptr[0]) << 8) | (((uint32_t)ptr[1]) << 0);
|
|
return value;
|
|
}
|
|
|
|
static void parse_sample(struct reader * r, struct sample * s)
|
|
{
|
|
s->name = reader_s(r, 22);
|
|
s->length = reader_u16(r);
|
|
s->nibble = reader_u8(r);
|
|
s->volume = reader_u8(r);
|
|
s->repeat_offset = reader_u16(r);
|
|
s->repeat_length = reader_u16(r);
|
|
}
|
|
|
|
static int parse_num_patterns(uint8_t * table)
|
|
{
|
|
int max = 0;
|
|
for (int i = 0; i < 128; i++) {
|
|
if ((int)table[i] > max) {
|
|
max = table[i];
|
|
}
|
|
}
|
|
return max + 1;
|
|
}
|
|
|
|
static void parse_channel(const uint8_t * data, struct channel * c)
|
|
{
|
|
uint32_t w = ((uint32_t)data[0] >> 4) & 0xf;
|
|
uint32_t x = ((uint32_t)data[1]) | (((uint32_t)data[0] & 0xf) << 8);
|
|
uint32_t y = ((uint32_t)data[2] >> 4) & 0xf;
|
|
uint32_t z = ((uint32_t)data[3]) | (((uint32_t)data[2] & 0xf) << 8);
|
|
|
|
c->sample = (w << 4) | y;
|
|
c->parameter = x;
|
|
c->effect = z;
|
|
}
|
|
|
|
static void parse_pattern(struct reader * r, struct pattern * pattern)
|
|
{
|
|
const uint8_t * raw_pattern = reader_s(r, 1024);
|
|
for (int division = 0; division < 64; division++) {
|
|
int ix = division * 16;
|
|
parse_channel(&raw_pattern[ix+0 ], &pattern->division[division][0]);
|
|
parse_channel(&raw_pattern[ix+4 ], &pattern->division[division][1]);
|
|
parse_channel(&raw_pattern[ix+8 ], &pattern->division[division][2]);
|
|
parse_channel(&raw_pattern[ix+12], &pattern->division[division][3]);
|
|
}
|
|
}
|
|
|
|
void parse_mod_file(struct reader * r, struct mod * mod)
|
|
{
|
|
mod->title = reader_s(r, 20);
|
|
|
|
for (int i = 0; i < 31; i++) {
|
|
parse_sample(r, &mod->samples[i]);
|
|
}
|
|
|
|
mod->song_positions = reader_u8(r);
|
|
mod->ignored = reader_u8(r);
|
|
reader_bytes(r, 128, mod->pattern_table);
|
|
|
|
assert(r->ix == 0x438);
|
|
reader_bytes(r, 4, mod->tag);
|
|
|
|
assert(mod->tag[0] == 'M' && mod->tag[1] == '.' &&
|
|
mod->tag[2] == 'K' && mod->tag[3] == '.');
|
|
|
|
int num_patterns = parse_num_patterns(mod->pattern_table);
|
|
for (int i = 0; i < num_patterns; i++) {
|
|
parse_pattern(r, &mod->patterns[i]);
|
|
}
|
|
}
|