mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-02-20 04:21:11 +01:00
Merge pull request #1658 from bnnm/musx-misc
- Fix some .sfx MUSX sample rate [Ice Ace 3 (PC), G-Force (PS3)] - Fix some .sch [Conflict: Desert Storm (PC)] - Add ADX key - Add extra .wave test codecs
This commit is contained in:
commit
30cad540c5
@ -83,23 +83,37 @@ class App(object):
|
||||
if line.startswith('#'):
|
||||
continue
|
||||
|
||||
if not line.endswith('.txtp'):
|
||||
if self.args.force:
|
||||
line += '.txtp'
|
||||
else:
|
||||
continue
|
||||
|
||||
line.replace('\\', '/')
|
||||
|
||||
line = line.replace('\\n', '\n') #may conflict with paths but...
|
||||
line = line.replace('\\', '/')
|
||||
|
||||
subdir = self.args.subdir
|
||||
if ':' in line:
|
||||
index = line.find(':') #internal txtp : txtp name
|
||||
# 'commands : name.txtp' format
|
||||
index = line.find(':')
|
||||
|
||||
text = line[0:index].strip()
|
||||
name = line[index+1:].strip()
|
||||
|
||||
# detect and allow 'name.txtp : commands' too
|
||||
if text.endswith('.txtp') and not name.endswith('.txtp'):
|
||||
temp = text
|
||||
text = name
|
||||
name = temp
|
||||
|
||||
if not name.endswith('.txtp'):
|
||||
if self.args.force:
|
||||
name += '.txtp'
|
||||
else:
|
||||
continue
|
||||
|
||||
elif self.args.maxitxtp or subdir:
|
||||
if not line.endswith('.txtp'):
|
||||
if self.args.force:
|
||||
line += '.txtp'
|
||||
else:
|
||||
continue
|
||||
|
||||
|
||||
index = line.find('.') #first extension
|
||||
|
||||
if line[index:].startswith('.txtp'): #???
|
||||
@ -114,6 +128,12 @@ class App(object):
|
||||
subdir = subdir + '/'
|
||||
text = subdir + text
|
||||
else:
|
||||
if not line.endswith('.txtp'):
|
||||
if self.args.force:
|
||||
line += '.txtp'
|
||||
else:
|
||||
continue
|
||||
|
||||
# should be a mini-txtp, but if name isn't "file.ext.txtp" and just "file.txtp",
|
||||
# probably means proper txtp exists and should't be created (when generating from !tags.m3u)
|
||||
name = line
|
||||
@ -128,6 +148,9 @@ class App(object):
|
||||
|
||||
outpath = os.path.join(path, name)
|
||||
|
||||
dir_path = os.path.dirname(outpath)
|
||||
os.makedirs(dir_path, exist_ok=True)
|
||||
|
||||
with open(outpath, 'w') as fo:
|
||||
if text:
|
||||
fo.write(text)
|
||||
|
20
doc/USAGE.md
20
doc/USAGE.md
@ -973,3 +973,23 @@ with `.txtp` as well.
|
||||
|
||||
This may even happen with formats that do have loops in other games (for example
|
||||
relatively common with `.fsb` and mobile games, that may define loops in a .json file).
|
||||
|
||||
|
||||
## Modding game audio and encoding wav files to video game formats
|
||||
vgmstream cannot *encode* (convert *from* `.wav` *to* a game format), it only *decodes*
|
||||
(plays game audio). It also can't repack/mod game files (like `.wem`) into other game
|
||||
formats (like `.bnk`).
|
||||
|
||||
One may think it's easy to do, since vgmstream reads game audio might as well write audio
|
||||
too, but *encoding* and *decoding* are very different.
|
||||
|
||||
To *decode* vgmstream just reads a few existing values from the file's *header*,
|
||||
to setup and play the file's *body* data, decompressing the game's audio codec.
|
||||
|
||||
To *encode* the program would need to make the *header* from scratch (having to include
|
||||
lots of values the game needs but aren't needed for vgmstream to play audio), and take
|
||||
PCM audio (.wav) and compress it (*very* different than decompressing) to make a *body*.
|
||||
|
||||
In other words you need a dedicated tool that can *encode* to your particular format.
|
||||
Since *encoding* is lot harder than *decoding* it's not very common to find public tools,
|
||||
and may need to program one yourself.
|
||||
|
@ -279,6 +279,9 @@ static const adxkey_info adxkey9_list[] = {
|
||||
/* ARGONAVIS -Kimi ga Mita Stage e- (Android) */
|
||||
{0x0000,0x0000,0x0000, NULL,301179795002661}, // 000111EBE2B1D525 (+ AWB subkeys)
|
||||
|
||||
// Bungo Stray Dogs: Mayoi Inu Kaikitan (iOS/Android)
|
||||
{0x0000,0x0000,0x0000, NULL,1655728931134731873}, // 16FA54B0C09F7661
|
||||
|
||||
};
|
||||
|
||||
static const int adxkey8_list_count = sizeof(adxkey8_list) / sizeof(adxkey8_list[0]);
|
||||
|
181
src/meta/musx.c
181
src/meta/musx.c
@ -25,6 +25,7 @@ typedef struct {
|
||||
int channels;
|
||||
int sample_rate;
|
||||
int loop_flag;
|
||||
uint32_t flags;
|
||||
int32_t loop_start;
|
||||
int32_t loop_end;
|
||||
int32_t num_samples;
|
||||
@ -173,7 +174,6 @@ fail:
|
||||
|
||||
static int parse_musx_stream(STREAMFILE* sf, musx_header* musx) {
|
||||
uint32_t (*read_u32)(off_t,STREAMFILE*) = NULL;
|
||||
int default_channels, default_sample_rate;
|
||||
|
||||
if (musx->big_endian) {
|
||||
read_u32 = read_u32be;
|
||||
@ -210,7 +210,85 @@ static int parse_musx_stream(STREAMFILE* sf, musx_header* musx) {
|
||||
}
|
||||
|
||||
|
||||
/* parse loops and other info */
|
||||
if (musx->tables_offset && musx->loops_offset) {
|
||||
/* cue/stream position table thing */
|
||||
/* 0x00: cues1 entries (entry size 0x34 or 0x18)
|
||||
* 0x04: cues2 entries (entry size 0x20 or 0x14)
|
||||
* 0x08: header size (always 0x14)
|
||||
* 0x0c: cues2 start
|
||||
* 0x10: volume? (usually <= 100) */
|
||||
|
||||
/* find loops (cues1 also seems to have this info but this looks ok) */
|
||||
int cues2_count = read_u32(musx->loops_offset+0x04, sf);
|
||||
off_t cues2_offset = musx->loops_offset + read_u32(musx->loops_offset+0x0c, sf);
|
||||
for (int i = 0; i < cues2_count; i++) {
|
||||
uint32_t type, offset1, offset2;
|
||||
|
||||
if (musx->is_old) {
|
||||
offset1 = read_u32(cues2_offset + i*0x20 + 0x04, sf);
|
||||
type = read_u32(cues2_offset + i*0x20 + 0x08, sf);
|
||||
offset2 = read_u32(cues2_offset + i*0x20 + 0x14, sf);
|
||||
} else {
|
||||
offset1 = read_u32(cues2_offset + i*0x14 + 0x04, sf);
|
||||
type = read_u32(cues2_offset + i*0x14 + 0x08, sf);
|
||||
offset2 = read_u32(cues2_offset + i*0x14 + 0x0c, sf);
|
||||
}
|
||||
|
||||
/* other types (0x0a, 0x09) look like section/end markers, 0x06/07 only seems to exist once */
|
||||
if (type == 0x06 || type == 0x07) { /* loop / goto */
|
||||
musx->loop_start = offset2;
|
||||
musx->loop_end = offset1;
|
||||
musx->loop_flag = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (musx->loops_offset && read_u32be(musx->loops_offset, sf) != 0xABABABAB) {
|
||||
/* parse loop table (loop starts are -1 if non-looping)
|
||||
* 0x00: version? (always 1)
|
||||
* 0x04: flags (&1=loops, &2=alt?)
|
||||
* 0x08: loop start offset?
|
||||
* 0x0c: loop end offset?
|
||||
* 0x10: loop end sample
|
||||
* 0x14: loop start sample
|
||||
* 0x18: loop end offset
|
||||
* 0x1c: loop start offset */
|
||||
musx->flags = read_u32le(musx->loops_offset+0x04, sf);
|
||||
musx->loop_end_sample = read_s32le(musx->loops_offset+0x10, sf);
|
||||
musx->loop_start_sample = read_s32le(musx->loops_offset+0x14, sf);
|
||||
musx->loop_end = read_s32le(musx->loops_offset+0x18, sf);
|
||||
musx->loop_start = read_s32le(musx->loops_offset+0x1c, sf);
|
||||
musx->num_samples = musx->loop_end_sample; /* preferable even if not looping as some files have padding */
|
||||
musx->loop_flag = (musx->loop_start_sample >= 0);
|
||||
}
|
||||
|
||||
/* fix some v10 platform (like PSP) sizes */
|
||||
if (musx->stream_size == 0) {
|
||||
musx->stream_size = musx->file_size - musx->stream_offset;
|
||||
|
||||
/* always padded to nearest 0x800 sector */
|
||||
if (musx->stream_size > 0x800) {
|
||||
uint8_t buf[0x800];
|
||||
int pos;
|
||||
off_t offset = musx->stream_offset + musx->stream_size - 0x800;
|
||||
|
||||
if (read_streamfile(buf, offset, sizeof(buf), sf) != 0x800)
|
||||
goto fail;
|
||||
|
||||
pos = 0x800 - 0x04;
|
||||
while (pos > 0) {
|
||||
if (get_u32be(buf + pos) != 0xABABABAB)
|
||||
break;
|
||||
musx->stream_size -= 0x04;
|
||||
pos -= 0x04;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* defaults */
|
||||
int default_channels, default_sample_rate;
|
||||
switch(musx->platform) {
|
||||
|
||||
case 0x5053325F: /* "PS2_" */
|
||||
@ -254,27 +332,27 @@ static int parse_musx_stream(STREAMFILE* sf, musx_header* musx) {
|
||||
break;
|
||||
|
||||
case 0x5749495F: /* "WII_" */
|
||||
default_channels = 2;
|
||||
default_sample_rate = 32000;
|
||||
musx->codec = DAT;
|
||||
break;
|
||||
|
||||
case 0x5053335F: /* "PS3_" */
|
||||
default_channels = 2;
|
||||
default_sample_rate = 44100;
|
||||
musx->codec = DAT;
|
||||
break;
|
||||
|
||||
case 0x58455F5F: /* "XE__" */
|
||||
default_channels = 2;
|
||||
default_sample_rate = 32000;
|
||||
musx->codec = DAT;
|
||||
break;
|
||||
|
||||
case 0x5053335F: /* "PS3_" */
|
||||
case 0x50435F5F: /* "PC__" */
|
||||
default_channels = 2;
|
||||
default_sample_rate = 44100;
|
||||
musx->codec = DAT;
|
||||
|
||||
// some v10 versions use 44100 and others 32000, the latter seem to have loop info table (even without loops) and a flag
|
||||
// - 44100: Robots (PC)-v10 (no loop table), Pirates of the Caribbean: At World's End (PC)-v10 (no loop table), Beijing 2008 (loop table)
|
||||
// - 32000: G-Force (PS3)-v10, Ice Age 3 (PC)-v10 (loop table with flag 2)
|
||||
// The flag also exists in files with similar loop tables in the DAT* chunk
|
||||
if (musx->version == 10 && musx->flags && musx->flags & 0x02) {
|
||||
default_sample_rate = 32000;
|
||||
}
|
||||
//TO-DO: some files use 22050 but don't seem to set any flag [Beijing 2008 (PS3)]
|
||||
|
||||
break;
|
||||
|
||||
case 0x50433032: /* "PC02" */
|
||||
@ -293,85 +371,6 @@ static int parse_musx_stream(STREAMFILE* sf, musx_header* musx) {
|
||||
if (musx->sample_rate == 0)
|
||||
musx->sample_rate = default_sample_rate;
|
||||
|
||||
|
||||
/* parse loops and other info */
|
||||
if (musx->tables_offset && musx->loops_offset) {
|
||||
int i, cues2_count;
|
||||
off_t cues2_offset;
|
||||
|
||||
/* cue/stream position table thing */
|
||||
/* 0x00: cues1 entries (entry size 0x34 or 0x18)
|
||||
* 0x04: cues2 entries (entry size 0x20 or 0x14)
|
||||
* 0x08: header size (always 0x14)
|
||||
* 0x0c: cues2 start
|
||||
* 0x10: volume? (usually <= 100) */
|
||||
|
||||
/* find loops (cues1 also seems to have this info but this looks ok) */
|
||||
cues2_count = read_u32(musx->loops_offset+0x04, sf);
|
||||
cues2_offset = musx->loops_offset + read_u32(musx->loops_offset+0x0c, sf);
|
||||
for (i = 0; i < cues2_count; i++) {
|
||||
uint32_t type, offset1, offset2;
|
||||
|
||||
if (musx->is_old) {
|
||||
offset1 = read_u32(cues2_offset + i*0x20 + 0x04, sf);
|
||||
type = read_u32(cues2_offset + i*0x20 + 0x08, sf);
|
||||
offset2 = read_u32(cues2_offset + i*0x20 + 0x14, sf);
|
||||
} else {
|
||||
offset1 = read_u32(cues2_offset + i*0x14 + 0x04, sf);
|
||||
type = read_u32(cues2_offset + i*0x14 + 0x08, sf);
|
||||
offset2 = read_u32(cues2_offset + i*0x14 + 0x0c, sf);
|
||||
}
|
||||
|
||||
/* other types (0x0a, 0x09) look like section/end markers, 0x06/07 only seems to exist once */
|
||||
if (type == 0x06 || type == 0x07) { /* loop / goto */
|
||||
musx->loop_start = offset2;
|
||||
musx->loop_end = offset1;
|
||||
musx->loop_flag = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (musx->loops_offset && read_u32be(musx->loops_offset, sf) != 0xABABABAB) {
|
||||
/* parse loop table (loop starts are -1 if non-looping)
|
||||
* 0x00: version?
|
||||
* 0x04: flags? (&1=loops)
|
||||
* 0x08: loop start offset?
|
||||
* 0x0c: loop end offset?
|
||||
* 0x10: loop end sample
|
||||
* 0x14: loop start sample
|
||||
* 0x18: loop end offset
|
||||
* 0x1c: loop start offset */
|
||||
musx->loop_end_sample = read_s32le(musx->loops_offset+0x10, sf);
|
||||
musx->loop_start_sample = read_s32le(musx->loops_offset+0x14, sf);
|
||||
musx->loop_end = read_s32le(musx->loops_offset+0x18, sf);
|
||||
musx->loop_start = read_s32le(musx->loops_offset+0x1c, sf);
|
||||
musx->num_samples = musx->loop_end_sample; /* preferable even if not looping as some files have padding */
|
||||
musx->loop_flag = (musx->loop_start_sample >= 0);
|
||||
}
|
||||
|
||||
/* fix some v10 platform (like PSP) sizes */
|
||||
if (musx->stream_size == 0) {
|
||||
musx->stream_size = musx->file_size - musx->stream_offset;
|
||||
|
||||
/* always padded to nearest 0x800 sector */
|
||||
if (musx->stream_size > 0x800) {
|
||||
uint8_t buf[0x800];
|
||||
int pos;
|
||||
off_t offset = musx->stream_offset + musx->stream_size - 0x800;
|
||||
|
||||
if (read_streamfile(buf, offset, sizeof(buf), sf) != 0x800)
|
||||
goto fail;
|
||||
|
||||
pos = 0x800 - 0x04;
|
||||
while (pos > 0) {
|
||||
if (get_u32be(buf + pos) != 0xABABABAB)
|
||||
break;
|
||||
musx->stream_size -= 0x04;
|
||||
pos -= 0x04;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
|
@ -10,20 +10,19 @@ VGMSTREAM* init_vgmstream_psf_single(STREAMFILE* sf) {
|
||||
off_t start_offset;
|
||||
int loop_flag, channel_count, sample_rate, rate_value, interleave;
|
||||
uint32_t psf_config;
|
||||
uint8_t flags;
|
||||
size_t data_size;
|
||||
coding_t codec;
|
||||
|
||||
|
||||
/* checks */
|
||||
if ((read_u32be(0x00,sf) & 0xFFFFFF00) != get_id32be("PSF\0"))
|
||||
goto fail;
|
||||
return NULL;
|
||||
/* .psf: actual extension
|
||||
* .swd: bigfile extension */
|
||||
if (!check_extensions(sf, "psf,swd"))
|
||||
goto fail;
|
||||
return NULL;
|
||||
|
||||
flags = read_8bit(0x03,sf);
|
||||
uint8_t flags = read_8bit(0x03,sf);
|
||||
switch(flags) {
|
||||
case 0xC0: /* [The Great Escape (PS2), Conflict: Desert Storm (PS2)] */
|
||||
case 0x40: /* [The Great Escape (PS2)] */
|
||||
@ -495,6 +494,8 @@ fail:
|
||||
|
||||
typedef enum { UNKNOWN, IMUS, PFST, PFSM } sch_type;
|
||||
#define SCH_STREAM "Stream.swd"
|
||||
#define SCH_STREAM_PS2 "STREAM.SWD"
|
||||
|
||||
|
||||
|
||||
/* SCH - Pivotal games multi-audio container [The Great Escape, Conflict series] */
|
||||
@ -504,7 +505,6 @@ VGMSTREAM* init_vgmstream_sch(STREAMFILE* sf) {
|
||||
STREAMFILE* temp_sf = NULL;
|
||||
off_t skip = 0, chunk_offset, target_offset = 0, header_offset, subfile_offset = 0;
|
||||
size_t file_size, chunk_padding, target_size = 0, subfile_size = 0;
|
||||
int big_endian;
|
||||
int total_subsongs = 0, target_subsong = sf->stream_index;
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
|
||||
sch_type target_type = UNKNOWN;
|
||||
@ -516,23 +516,23 @@ VGMSTREAM* init_vgmstream_sch(STREAMFILE* sf) {
|
||||
skip = 0x0E;
|
||||
if (!is_id32be(skip + 0x00,sf, "SCH\0") &&
|
||||
!is_id32le(skip + 0x00,sf, "SCH\0")) /* (BE consoles) */
|
||||
goto fail;
|
||||
return NULL;
|
||||
|
||||
if (!check_extensions(sf, "sch"))
|
||||
goto fail;
|
||||
return NULL;
|
||||
|
||||
|
||||
/* chunked format (id+size, GC pads to 0x20 and uses BE/inverted ids):
|
||||
* no other info so total subsongs would be count of usable chunks
|
||||
* (offsets are probably in level .dat files) */
|
||||
big_endian = (is_id32le(skip + 0x00,sf, "SCH\0"));
|
||||
int big_endian = (is_id32le(skip + 0x00,sf, "SCH\0"));
|
||||
if (big_endian) {
|
||||
read_32bit = read_32bitBE;
|
||||
chunk_padding = 0x18;
|
||||
}
|
||||
else {
|
||||
read_32bit = read_32bitLE;
|
||||
chunk_padding = 0;
|
||||
chunk_padding = 0x00;
|
||||
}
|
||||
|
||||
file_size = get_streamfile_size(sf);
|
||||
@ -598,9 +598,9 @@ VGMSTREAM* init_vgmstream_sch(STREAMFILE* sf) {
|
||||
|
||||
switch(target_type) {
|
||||
case IMUS: { /* external segmented track */
|
||||
STREAMFILE *psf_sf;
|
||||
STREAMFILE* psf_sf = NULL;
|
||||
uint8_t name_size;
|
||||
char name[255];
|
||||
char name[256];
|
||||
|
||||
/* 0x00: config/size?
|
||||
* 0x04: name size
|
||||
@ -611,32 +611,33 @@ VGMSTREAM* init_vgmstream_sch(STREAMFILE* sf) {
|
||||
*/
|
||||
|
||||
name_size = read_u8(header_offset + 0x04, sf);
|
||||
read_string(name,name_size, header_offset + 0x08, sf);
|
||||
read_string(name, name_size, header_offset + 0x08, sf);
|
||||
|
||||
/* later games have name but actually use bigfile [Conflict: Global Storm (Xbox)] */
|
||||
/* later Xbox games have name but actually use bigfile [Conflict: Global Storm (Xbox)] */
|
||||
if (read_u8(header_offset + 0x07, sf) == 0xCC) {
|
||||
external_sf = open_streamfile_by_filename(sf, SCH_STREAM);
|
||||
if (!external_sf) {
|
||||
vgm_logi("SCH: external file '%s' not found (put together)\n", SCH_STREAM);
|
||||
goto fail;
|
||||
if (external_sf) {
|
||||
subfile_offset = read_32bit(header_offset + 0x08 + name_size, sf);
|
||||
subfile_size = get_streamfile_size(external_sf) - subfile_offset; /* not ok but meh */
|
||||
|
||||
temp_sf = setup_subfile_streamfile(external_sf, subfile_offset,subfile_size, "psf");
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
psf_sf = temp_sf;
|
||||
}
|
||||
|
||||
subfile_offset = read_32bit(header_offset + 0x08 + name_size, sf);
|
||||
subfile_size = get_streamfile_size(external_sf) - subfile_offset; /* not ok but meh */
|
||||
|
||||
temp_sf = setup_subfile_streamfile(external_sf, subfile_offset,subfile_size, "psf");
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
psf_sf = temp_sf;
|
||||
}
|
||||
else {
|
||||
external_sf = open_streamfile_by_filename(sf, name);
|
||||
if (!external_sf) {
|
||||
vgm_logi("SCH: external file '%s' not found (put together)\n", name);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
psf_sf = external_sf;
|
||||
/* PC games still use name + 0xCC at header, no diffs vs Xbox? [Conflict: Global Storm (PC)] */
|
||||
if (!psf_sf) {
|
||||
external_sf = open_streamfile_by_filename(sf, name);
|
||||
if (external_sf) {
|
||||
psf_sf = external_sf;
|
||||
}
|
||||
}
|
||||
|
||||
if (!psf_sf) {
|
||||
vgm_logi("SCH: external file '%s' or '%s' not found (put together)\n", SCH_STREAM, name);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
vgmstream = init_vgmstream_psf_segmented(psf_sf);
|
||||
@ -652,7 +653,7 @@ VGMSTREAM* init_vgmstream_sch(STREAMFILE* sf) {
|
||||
case PFST: { /* external track */
|
||||
STREAMFILE *psf_sf;
|
||||
uint8_t name_size;
|
||||
char name[255];
|
||||
char name[256];
|
||||
|
||||
if (chunk_padding == 0 && target_size > 0x08 + 0x0c) { /* TGE PC/Xbox version */
|
||||
/* 0x00: -1/0
|
||||
@ -697,7 +698,7 @@ VGMSTREAM* init_vgmstream_sch(STREAMFILE* sf) {
|
||||
}
|
||||
}
|
||||
else if (chunk_padding) {
|
||||
strcpy(name, "STREAM.SWD"); /* fixed */
|
||||
strcpy(name, SCH_STREAM_PS2); /* fixed */
|
||||
|
||||
/* 0x00: -1
|
||||
* 0x04: config/size?
|
||||
@ -715,7 +716,7 @@ VGMSTREAM* init_vgmstream_sch(STREAMFILE* sf) {
|
||||
psf_sf = temp_sf;
|
||||
}
|
||||
else { /* others */
|
||||
strcpy(name, "STREAM.SWD"); /* fixed */
|
||||
strcpy(name, SCH_STREAM_PS2); /* fixed */
|
||||
|
||||
/* 0x00: -1
|
||||
* 0x04: config/size?
|
||||
|
@ -854,7 +854,7 @@ static int add_entry(txtp_header_t* txtp, char* filename, int is_default) {
|
||||
txtp_entry_t entry = {0};
|
||||
|
||||
|
||||
;VGM_LOG("TXTP: input filename=%s\n", filename);
|
||||
//;VGM_LOG("TXTP: input filename=%s\n", filename);
|
||||
|
||||
/* parse filename: file.ext#(commands) */
|
||||
{
|
||||
@ -896,11 +896,11 @@ static int add_entry(txtp_header_t* txtp, char* filename, int is_default) {
|
||||
params = NULL;
|
||||
}
|
||||
|
||||
;VGM_LOG("TXTP: params=%s\n", params);
|
||||
//;VGM_LOG("TXTP: params=%s\n", params);
|
||||
parse_params(&entry, params);
|
||||
}
|
||||
|
||||
;VGM_LOG("TXTP: output filename=%s\n", filename);
|
||||
//;VGM_LOG("TXTP: output filename=%s\n", filename);
|
||||
|
||||
clean_filename(filename);
|
||||
//;VGM_LOG("TXTP: clean filename='%s'\n", filename);
|
||||
|
@ -8,7 +8,6 @@ VGMSTREAM* init_vgmstream_wave(STREAMFILE* sf) {
|
||||
uint32_t start_offset, extradata_offset, interleave;
|
||||
int channels, loop_flag, sample_rate, codec, version;
|
||||
int32_t num_samples, loop_start, loop_end;
|
||||
int big_endian;
|
||||
read_u32_t read_u32;
|
||||
read_s32_t read_s32;
|
||||
read_f32_t read_f32;
|
||||
@ -25,7 +24,7 @@ VGMSTREAM* init_vgmstream_wave(STREAMFILE* sf) {
|
||||
if (!check_extensions(sf, "wave"))
|
||||
return NULL;
|
||||
|
||||
big_endian = read_u32be(0x00,sf) == 0xE5B7ECFE || is_id32be(0x00,sf, "WWAV");
|
||||
bool big_endian = read_u32be(0x00,sf) == 0xE5B7ECFE || is_id32be(0x00,sf, "WWAV");
|
||||
if (big_endian) {
|
||||
read_u32 = read_u32be;
|
||||
read_s32 = read_s32be;
|
||||
@ -46,7 +45,7 @@ VGMSTREAM* init_vgmstream_wave(STREAMFILE* sf) {
|
||||
loop_end = read_s32(0x18, sf);
|
||||
|
||||
codec = read_u8(0x1c, sf);
|
||||
channels = read_u8(0x1d, sf);
|
||||
channels = read_u8(0x1d, sf); // DS can only do mono
|
||||
if (read_u8(0x1e, sf) != 0x00) goto fail; /* unknown */
|
||||
if (read_u8(0x1f, sf) != 0x00) goto fail; /* unknown */
|
||||
|
||||
@ -60,14 +59,19 @@ VGMSTREAM* init_vgmstream_wave(STREAMFILE* sf) {
|
||||
if(!loop_flag
|
||||
&& loop_start == 0 && loop_end == num_samples /* full loop */
|
||||
&& (channels > 1 || (channels == 1 && start_offset <= 0x40))
|
||||
&& num_samples > 30*sample_rate) { /* in seconds */
|
||||
&& num_samples > 30 * sample_rate) { /* in seconds */
|
||||
loop_flag = 1;
|
||||
}
|
||||
|
||||
/* normalize codec: WWAV uses codec 0x00 for DSP */
|
||||
/* normalize codec (files generated by DsBuildWave/3dsBuildWave) */
|
||||
if (codec == 0x00 && version == 0x00050000 && start_offset > 0x40) {
|
||||
/* WWAV uses codec 0x00 for DSP (only one?) */
|
||||
codec = 0x02;
|
||||
}
|
||||
else if (codec == 0x02 && start_offset <= 0x40) {
|
||||
/* DS games use IMA, no apparent flag (could also test ID) */
|
||||
codec = 0x03;
|
||||
}
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
@ -81,39 +85,51 @@ VGMSTREAM* init_vgmstream_wave(STREAMFILE* sf) {
|
||||
|
||||
vgmstream->meta_type = meta_WAVE;
|
||||
|
||||
/* not sure if there are other codecs but anyway (based on wave-segmented) */
|
||||
/* some codecs aren't used by known games but can be created by DsBuildWave/3dsBuildWave */
|
||||
switch(codec) {
|
||||
case 0x02:
|
||||
/* DS games use IMA, no apparent flag (could also test ID) */
|
||||
if (start_offset <= 0x40) {
|
||||
vgmstream->coding_type = coding_IMA_int;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = interleave;
|
||||
|
||||
/* extradata:
|
||||
* 0x00: base hist? (only seen 0)
|
||||
* 0x02: base step? (only seen 0)
|
||||
* 0x04: loop hist?
|
||||
* 0x06: loop step?
|
||||
*/
|
||||
}
|
||||
else {
|
||||
vgmstream->coding_type = coding_NGC_DSP;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = interleave;
|
||||
|
||||
/* ADPCM setup: 0x20 coefs + 0x06 initial ps/hist1/hist2 + 0x06 loop ps/hist1/hist2 + ?, per channel */
|
||||
int head_spacing = 0x2c;
|
||||
int hist_spacing = 0x22;
|
||||
if (version == 0x00050000) { /* has an extra empty 16b after coefs */
|
||||
head_spacing = 0x2e;
|
||||
hist_spacing = 0x24;
|
||||
}
|
||||
|
||||
dsp_read_coefs(vgmstream, sf, extradata_offset + 0x00, head_spacing, big_endian);
|
||||
dsp_read_hist(vgmstream, sf, extradata_offset + hist_spacing, head_spacing, big_endian);
|
||||
}
|
||||
case 0x00: // PCM8 (not seen)
|
||||
vgmstream->coding_type = coding_PCM8;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = interleave;
|
||||
break;
|
||||
|
||||
case 0x01: // PCM16 (not seen)
|
||||
vgmstream->coding_type = coding_PCM16BE;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = interleave;
|
||||
break;
|
||||
|
||||
case 0x02: { // DSP (3DS only, common)
|
||||
vgmstream->coding_type = coding_NGC_DSP;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = interleave;
|
||||
|
||||
/* ADPCM setup: 0x20 coefs + 0x06 initial ps/hist1/hist2 + 0x06 loop ps/hist1/hist2 + ?, per channel */
|
||||
int head_spacing = 0x2c;
|
||||
int hist_spacing = 0x22;
|
||||
if (version == 0x00050000) { /* has an extra empty 16b after coefs */
|
||||
head_spacing = 0x2e;
|
||||
hist_spacing = 0x24;
|
||||
}
|
||||
|
||||
dsp_read_coefs(vgmstream, sf, extradata_offset + 0x00, head_spacing, big_endian);
|
||||
dsp_read_hist(vgmstream, sf, extradata_offset + hist_spacing, head_spacing, big_endian);
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x03: //IMA (DS uses codec 02 for IMA, common; 3DS: uses 03 but not seen)
|
||||
vgmstream->coding_type = coding_IMA_int;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = interleave;
|
||||
|
||||
/* extradata:
|
||||
* 0x00: base hist? (only seen 0)
|
||||
* 0x02: base step? (only seen 0)
|
||||
* 0x04: loop hist?
|
||||
* 0x06: loop step?
|
||||
*/
|
||||
break;
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user