dreamcast/xm/debug.c

329 lines
9.9 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "xm.h"
int read_file(const char * filename, void ** buf, uint32_t * size_out)
{
FILE * file = fopen(filename, "rb");
if (file == NULL) {
fprintf(stderr, "fopen(\"%s\", \"rb\"): %s\n", filename, strerror(errno));
return -1;
}
int ret;
ret = fseek(file, 0L, SEEK_END);
if (ret < 0) {
fprintf(stderr, "fseek(SEEK_END)");
return -1;
}
long offset = ftell(file);
if (offset < 0) {
fprintf(stderr, "ftell");
return -1;
}
size_t size = offset;
ret = fseek(file, 0L, SEEK_SET);
if (ret < 0) {
fprintf(stderr, "fseek(SEEK_SET)");
return -1;
}
fprintf(stderr, "read_file: %s size %ld\n", filename, size);
*buf = (uint8_t *)malloc(size);
size_t fread_size = fread(*buf, 1, size, file);
if (fread_size != size) {
fprintf(stderr, "fread `%s` short read: %d ; expected: %d\n", filename, (int)fread_size, (int)size);
return -1;
}
ret = fclose(file);
if (ret < 0) {
fprintf(stderr, "fclose");
return -1;
}
*size_out = size;
return 0;
}
int s16(void * buf)
{
uint8_t * b = (uint8_t *)buf;
int16_t v = (b[0] << 0) | (b[1] << 8);
return v;
}
int s32(void * buf)
{
uint8_t * b = (uint8_t *)buf;
int32_t v = (b[0] << 0) | (b[1] << 8) | (b[2] << 16) | (b[3] << 24);
return v;
}
void print_chars(int8_t * chars, int length, const char * end)
{
for (int i = 0; i < length; i++) {
int8_t c = chars[i];
if (c >= 0x20 && c <= 0x7e) {
fputc(c, stdout);
} else {
printf("\\x%02x", c);
}
}
if (end != NULL)
fputs(end, stdout);
}
void debug_header(void * buf)
{
xm_header_t * header = (xm_header_t *)buf;
printf("header:\n");
printf(" id_text: '");
print_chars(header->id_text, 17, "'\n");
printf(" module_name: '");
print_chars(header->module_name, 20, "'\n");
printf(" xm_type: 0x%02x\n", header->xm_type);
printf(" tracker_name: '");
print_chars(header->tracker_name, 20, "'\n");
printf(" version_number: 0x%04x\n", s16(&header->version_number));
printf(" header_size: %d\n", s32(&header->header_size));
printf(" song_length: %d\n", s16(&header->song_length));
printf(" restart_position: %d\n", s16(&header->restart_position));
printf(" number_of_channels: %d\n", s16(&header->number_of_channels));
printf(" number_of_patterns: %d\n", s16(&header->number_of_patterns));
printf(" number_of_instruments: %d\n", s16(&header->number_of_instruments));
printf(" flags: %d\n", s16(&header->flags));
printf(" default_tempo: %d\n", s16(&header->default_tempo));
printf(" default_bpm: %d\n", s16(&header->default_bpm));
}
static xm_pattern_format_t column[8];
void debug_pattern_format(int note_ix, xm_pattern_format_t * pf)
{
/*
printf("note[%d]\n", note_ix);
printf(" note: %d\n", pf->note);
printf(" instrument: %d\n", pf->instrument);
printf(" volume_column_byte: %d\n", pf->volume_column_byte);
printf(" effect_type: %d\n", pf->effect_type);
printf(" effect_parameter: %d\n", pf->effect_parameter);
*/
column[note_ix & 7] = *pf;
if ((note_ix & 7) == 7) {
printf("%3d |", note_ix / 8);
for (int i = 0; i < 8; i++)
printf(" n:%2d i:%2d |",
column[i].note, column[i].instrument);
printf("\n");
}
}
void debug_pattern(xm_pattern_header_t * pattern_header)
{
printf(" | channel 0 | channel 1 | channel 2 | channel 3 | channel 4 | channel 5 | channel 6 | channel 7 |\n");
uint8_t * pattern = (uint8_t *)(((ptrdiff_t)pattern_header) + s32(&pattern_header->pattern_header_length));
int ix = 0;
int note_ix = 0;
while (ix < s16(&pattern_header->packed_pattern_data_size)) {
int p = pattern[ix];
if (p & 0x80) {
ix += 1;
xm_pattern_format_t pf = {};
if (p & (1 << 0))
pf.note = pattern[ix++];
if (p & (1 << 1))
pf.instrument = pattern[ix++];
if (p & (1 << 2))
pf.volume_column_byte = pattern[ix++];
if (p & (1 << 3))
pf.effect_type = pattern[ix++];
if (p & (1 << 4))
pf.effect_parameter = pattern[ix++];
debug_pattern_format(note_ix, &pf);
} else {
xm_pattern_format_t * pf = (xm_pattern_format_t *)&pattern[ix];
debug_pattern_format(note_ix, pf);
ix += 5;
}
note_ix += 1;
}
assert(ix == s16(&pattern_header->packed_pattern_data_size));
}
int debug_pattern_headers(void * buf)
{
xm_header_t * header = (xm_header_t *)buf;
int pattern_header_offset = s32(&header->header_size) + (offsetof (struct xm_header, header_size));
printf("pattern number of patterns: %08x\n", s16(&header->number_of_patterns));
printf("pattern header offset: %08x\n", pattern_header_offset);
for (int i = 0; i < s16(&header->number_of_patterns); i++) {
xm_pattern_header_t * pattern_header = (xm_pattern_header_t *)(((ptrdiff_t)buf) + pattern_header_offset);
printf("pattern_header[%d]:\n", i);
printf(" pattern_header_length: %d\n", s32(&pattern_header->pattern_header_length));
printf(" packing_type: %d\n", pattern_header->packing_type);
printf(" number_of_rows_in_pattern: %d\n", s16(&pattern_header->number_of_rows_in_pattern));
printf(" packed_pattern_data_size: %d\n", s16(&pattern_header->packed_pattern_data_size));
//debug_pattern(pattern_header);
pattern_header_offset += s32(&pattern_header->pattern_header_length) + s16(&pattern_header->packed_pattern_data_size);
printf("pattern header offset: %08x\n", pattern_header_offset);
}
return pattern_header_offset;
}
void write_file(const char * filename, void * buf, int size)
{
printf("write %s\n", filename);
FILE * file = fopen(filename, "wb");
if (file == NULL) {
fprintf(stderr, "fopen(\"%s\", \"wb\"): %s\n", filename, strerror(errno));
return;
}
size_t write = fwrite(buf, 1, size, file);
assert(write == size);
int ret = fclose(file);
assert(ret == 0);
}
int saturation16(int v)
{
/*
if (v > 32767)
return 32767;
if (v < -32768)
return -32768;
*/
return v;
}
int saturation8(int v)
{
/*
if (v > 127)
return 127;
if (v < -128)
return -128;
*/
return v;
}
void dump_sample(void * buf, int offset, int sample_ix, xm_sample_header_t * sample_header)
{
assert(sample_header->sample_data_type == 0);
int old = 0;
int size = s32(&sample_header->sample_length);
printf("%d offset %d\n", sample_ix, offset);
if (sample_header->type & (1 << 4)) { // 16-bit samples
int num_samples = size / 2;
int old = 0;
int16_t out[num_samples];
int16_t * in = (int16_t *)(((ptrdiff_t)buf) + offset);
for (int i = 0; i < num_samples; i++) {
old += in[i];
out[i] = saturation16(old);
}
char filename[64];
snprintf(filename, 64, "sample%03d.s16le.pcm", sample_ix);
write_file(filename, out, size);
} else { // 8-bit
int num_samples = size;
int old = 0;
int8_t out[num_samples];
int8_t * in = (int8_t *)(((ptrdiff_t)buf) + offset);
for (int i = 0; i < num_samples; i++) {
old += in[i];
out[i] = old;
}
char filename[64];
snprintf(filename, 64, "sample%03d.s8.pcm", sample_ix);
write_file(filename, out, size);
}
}
int debug_samples(void * buf, int offset, int instrument_ix, int number_of_samples)
{
xm_sample_header_t * sample_header[number_of_samples];
printf("A offset %d\n", offset);
for (int i = 0; i < number_of_samples; i++) {
sample_header[i] = (xm_sample_header_t *)(((ptrdiff_t)buf) + offset);
printf(" sample header %d offset %d\n", i, offset);
printf(" sample[%d]\n", i);
printf(" sample_length: %d\n", s32(&sample_header[i]->sample_length));
printf(" sample_loop_start: %d\n", s32(&sample_header[i]->sample_loop_start));
printf(" sample_loop_length: %d\n", s32(&sample_header[i]->sample_loop_length));
printf(" volume: %d\n", sample_header[i]->volume);
printf(" finetune: %d\n", sample_header[i]->finetune);
printf(" type: %d\n", sample_header[i]->type);
printf(" panning: %d\n", sample_header[i]->panning);
printf(" relative_note_number: %d\n", sample_header[i]->relative_note_number);
printf(" sample_data_type: %d\n", sample_header[i]->sample_data_type);
printf(" sample_name: '");
print_chars(sample_header[i]->sample_name, 22, "'\n");
offset += (sizeof (xm_sample_header_t));
}
printf("B offset %d\n", offset);
for (int i = 0; i < number_of_samples; i++) {
if (s32(&sample_header[i]->sample_length) > 0)
dump_sample(buf, offset, instrument_ix, sample_header[i]);
offset += s32(&sample_header[i]->sample_length);
}
return offset;
}
int debug_instruments(void * buf, int offset)
{
xm_header_t * header = (xm_header_t *)buf;
for (int i = 0; i < s16(&header->number_of_instruments); i++) {
printf("instrument offset %d: %d\n", i, offset);
xm_instrument_header_t * instrument_header = (xm_instrument_header_t *)(((ptrdiff_t)buf) + offset);
printf("instrument[%d]\n", i);
printf(" instrument_size: %d\n", s32(&instrument_header->instrument_size));
printf(" instrument_name: '");
print_chars(instrument_header->instrument_name, 22, "'\n");
printf(" instrument_type: %d\n", instrument_header->instrument_type);
printf(" number_of_samples: %d\n", s16(&instrument_header->number_of_samples));
offset += s32(&instrument_header->instrument_size);
printf("this offset %d\n", offset);
if (s16(&instrument_header->number_of_samples) > 0) {
printf(" sample_header_size: %d\n", s32(&instrument_header->sample_header_size));
offset = debug_samples(buf, offset, i, s16(&instrument_header->number_of_samples));
}
}
return offset;
}
int main(int argc, const char *argv[])
{
assert(argc == 2);
const char * filename = argv[1];
void * buf;
uint32_t size;
int res = read_file(filename, &buf, &size);
if (res != 0)
return EXIT_FAILURE;
debug_header(buf);
int end_of_patterns = debug_pattern_headers(buf);
int end_of_instruments = debug_instruments(buf, end_of_patterns);
printf("end_of_instruments: %08x\n", end_of_instruments);
}