Add MFP file format description

Signed-off-by: Claudio Matsuoka <cmatsuoka@gmail.com>
This commit is contained in:
Claudio Matsuoka
2010-01-02 14:35:55 -02:00
parent 3a26919f4b
commit c99740bbdd
5 changed files with 131 additions and 13 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
xmp/doc/format/digi_format
xmp/docs/format/digi_format
analysed by Claudio Matsuoka <claudio@helllabs.org>
Sun Feb 21 18:20:32 EST 1999
+112
View File
@@ -0,0 +1,112 @@
Magnetic Fields Packer file format
Format created by Shaun Southern
Format description by Claudio Matsuoka <cmatsuoka@gmail.com>, 02-Jan-2010
The Magnetic Fields Packer format is a packed four channel module format
used in games such as Crystal Dragon, Kid Chaos and Tower of Souls. Each
module comes in two separate files, the first containing instrument and
pattern sequencing information, and the second containing all the samples.
File prefix is mfp. Samples file prefix is smp. Many mfp files can share
the same sample files, in Kid Chaos file names are like mfp.level_1-1 and
mfp.level_1-2 for the song file and smp.level_1.set for the samples file.
For more information: http://www.exotica.org.uk/wiki/Magnetic_Fields_Packer
Offset Size Comment
------ ------ ---------------------------------------------
0000 8*31 31 instruments definition, 8 bytes per
instrument as follows:
LL FV SS ZZ
| || | |
| || | +---- loop size in words
| || +------- loop start in words
| |+--------- volume
| +---------- finetune
+------------- sample length in words
00f8 1 Song length in patterns
00f9 1 Restart or "Noisetracker byte" (0x7f)
00fa 128 Pattern order
017a 2 Size of the pattern definition table
017c 2 ??? Maybe song length (same as above)
017e pat*4 Pattern table with pointers to track data
(4 words per pattern, offset 0 is at the end of
the pattern table)
+pat*4 varies Compressed track data
Compressed track data works with triple indirection track tables. Each
track starts at the offset given in the pattern table, with the first
track starting at offset 0. Each track table has four one-byte entries
containing a pointer to the next level or to note data, relative to the
level 1 track table.
Example:
Raw data: 04 08 0C 08 10 14 18 1C 1C 1C 1C 1C 20 1C 1C 1C
12 14 16 16 16 16 16 16 16 14 14 14 14 14 14 14
18 14 14 14 01 53 2C 00 00 00 00 00 00 00 0A 10
01 94 2C 28
Level 1 Level 2 Level 3 Notes
(offset 00) (offset 04) (offset 10) (offset 24)
+----+ +----+ +----+ x2 +------------+
| 04 | ----------> | 10 | ----------> | 12 | --------> | E 2 02 C00 |
+----+ +----+ +----+ +------------+
| 08 | -------+ | 14 | -------+ | 14 | ------+
+----+ | +----+ | +----+ | (offset 28)
| 0C | ---+ | | 18 | | | 16 | ---+ | +------------+
+----+ | | +----+ | +----+ | +-> | --- -- --- |
| 08 | | | | 1C | | | 16 | ---+ +------------+
+----+ . | +----+ | +----+ |
. | | | (offset 2C)
. | (offset 08) | (offset 14) | +------------+
| +----+ | +----+ +----> | --- -- A10 |
+--> | 1C | +--> | 16 | ---+ +------------+
+----+ +----+ |
| 1C | | 16 | ---+ (offset 30)
+----+ +----+ | +------------+
| 1C | | 16 | ---+ | C#2 02 C28 |
+----+ +----+ | +------------+
| 1C | | 16 | ---+
+----+ +----+
Example 2: (empty track)
Raw data: 04 04 04 04 08 08 08 08 06 06 06 06 00 00 00 00
Level 1 Level 2 Level 3 Notes
(offset 00) (offset 04) (offset 08) (offset 0C)
+----+ +----+ +----+ x2 +------------+
| 04 | -----+----> | 08 | -----+----> | 06 | -----+--> | --- -- --- |
+----+ | +----+ | +----+ | +------------+
| 04 | -----+ | 08 | -----+ | 06 | -----+
+----+ | +----+ | +----+ |
| 04 | -----+ | 08 | -----+ | 06 | -----+
+----+ | +----+ | +----+ |
| 04 | -----+ | 08 | -----+ | 06 | -----+
+----+ +----+ +----+
Pseudo-code to depack patterns:
for (pattern = 0; pattern < num_patterns; pattern++) {
for (voice = 0; voice < 4; voice++) {
read buffer at pattern_address + pattern_table[pattern][voice];
for (row = k = 0; k < 4; k++) { /* iterate first indirection table */
for (x = 0; x < 4; x++) { /* iterate second indirection table */
for (y = 0; y < 4; y++) { /* iterate third indirection table */
note_event = buffer[buffer[buffer[buffer[k] + x] + y] * 2];
}
}
}
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
xmp/doc/format/sfx_format
xmp/docs/format/sfx_format
analysed by Claudio Matsuoka <claudio@helllabs.org>
Mon Feb 22 09:29:58 EST 1999
+1 -1
View File
@@ -1,4 +1,4 @@
xmp/doc/format/stx_format
xmp/docs/format/stx_format
analysed by Claudio Matsuoka <claudio@helllabs.org>
Format: STX
+16 -10
View File
@@ -35,7 +35,7 @@ struct xmp_loader_info mfp_loader = {
static int mfp_test(FILE *f, char *t, const int start)
{
uint8 buf[384];
int i, len, lps, loop_size;
int i, len, lps, lsz;
if (fread(buf, 1, 384, f) < 384)
return -1;
@@ -44,10 +44,6 @@ static int mfp_test(FILE *f, char *t, const int start)
if (buf[249] != 0x7f)
return -1;
/* check song length */
if (buf[248] > 0x7f)
return -1;
for (i = 0; i < 31; i++) {
/* check size */
len = readmem16b(buf + i * 8);
@@ -68,14 +64,17 @@ static int mfp_test(FILE *f, char *t, const int start)
return -1;
/* check loop size */
loop_size = readmem16b(buf + i * 8 + 6);
if (lps + loop_size - 1 > len)
lsz = readmem16b(buf + i * 8 + 6);
if (lps + lsz - 1 > len)
return -1;
if (loop_size == 0)
if (lsz == 0)
return -1;
}
if (buf[248] != readmem16b(buf + 378))
return -1;
if (readmem16b(buf + 378) != readmem16b(buf + 380))
return -1;
@@ -136,15 +135,21 @@ static int mfp_load(struct xmp_context *ctx, FILE *f, const int start)
}
}
m->xxh->len = read8(f);
m->xxh->len = m->xxh->pat = read8(f);
read8(f); /* restart */
for (i = 0; i < 128; i++)
m->xxo[i] = read8(f);
#if 0
for (i = 0; i < 128; i++) {
m->xxo[i] = read8(f);
if (m->xxo[i] > m->xxh->pat)
m->xxh->pat = m->xxo[i];
}
m->xxh->pat++;
#endif
m->xxh->trk = m->xxh->pat * m->xxh->chn;
/* Read and convert patterns */
@@ -155,8 +160,9 @@ static int mfp_load(struct xmp_context *ctx, FILE *f, const int start)
size2 = read16b(f);
for (i = 0; i < size1; i++) { /* Read pattern table */
for (j = 0; j < 4; j++)
for (j = 0; j < 4; j++) {
pat_table[i][j] = read16b(f);
}
}
reportv(ctx, 0, "Stored patterns: %d ", m->xxh->pat);