Merge pull request #1542 from bnnm/msima-xnb

- Add TXTP frame_size + interleave for MS_IMA [Manhunt (PC)]
- Fix some .xnb [Miner: Dig Deep (X360)]
This commit is contained in:
bnnm 2024-06-07 13:51:03 +02:00 committed by GitHub
commit fa68185746
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 50 additions and 16 deletions

View File

@ -2,13 +2,13 @@
This is vgmstream, a library for playing streamed (prerecorded) video game audio.
Some of vgmstream's features:
- [Hundreds of video game music formats and codecs](doc/FORMATS.md), from typical game engine files
to obscure single-game codecs, aiming for high accuracy and compatibility.
- Decodes [hundreds of video game music formats and codecs](doc/FORMATS.md), from typical
game engine files to obscure single-game codecs, aiming for high accuracy and compatibility.
- Support for looped BGM, using file's internal metadata for smooth transitions, with accurate
sample counts.
- [Subsongs](doc/USAGE.md#subsongs), playing a format's multiple internal songs separately.
- Many types of companion files (data split into multiple files) and custom containers.
- Encryption keys, internal stream names, and many other unusual cases found in game audio.
- Encryption keys, internal stream names, and other unusual cases found in game audio.
- [TXTH](doc/TXTH.md) function, to add external support for extra formats, including raw audio in
many forms.
- [TXTP](doc/TXTP.md) function, for real-time and per-file config, like forced looping, removing

View File

@ -125,6 +125,7 @@ as explained below, but often will use default values. Accepted codec strings:
# - MS_IMA Microsoft IMA ADPCM
# * For some PC games
# * frame_size (or interleave) varies, often multiple of 0x100 [required]
# * frame_size + interleave forces mono mode
# - APPLE_IMA4 Apple Quicktime IMA ADPCM
# * For some Mac/iOS games
# - IMA_HV High Voltage's IMA ADPCM

View File

@ -929,3 +929,25 @@ vgmstream's internals are tailored to play streams so, in other words, it's not
possible to add support for sequenced audio unless massive changes were done,
basically becoming another program entirely. There are other projects better
suited for playing sequences.
## External loop points
Most games use audio formats that define loop points inside its files. That is,
you get looped/repeated audio in vgmstream simply by opening the files.
However some games use formats that don't define loops points, and instead store
loops in the executable or some external file. For example they could have a bunch
of `.ogg` and some text with start/end loop time info for all `.ogg`, or `.opus`
files with loop samples defined in a `.bfsar`.
Since those cases are typically custom/per game, vgmstream can't really read those
loop points automatically. Instead, one should make (manually or with some script)
one TXTP per file that tells vgmstream about its external loop points, and play
the `.txtp`:
**BGM_BTL_ACMaster_opus.txtp**: `BGM_BTL_ACMaster_opus.lopus #I 258724 2929972`
Some games also use intro + loop "segments" in separate files that can be combined
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).

View File

@ -461,14 +461,24 @@ VGMSTREAM* init_vgmstream_txth(STREAMFILE* sf) {
break;
case coding_MS_IMA:
vgmstream->interleave_block_size = txth.frame_size ? txth.frame_size : txth.interleave;
vgmstream->layout_type = layout_none;
if (txth.interleave && txth.frame_size) {
coding = coding_MS_IMA_mono;
vgmstream->frame_size = txth.frame_size;
vgmstream->interleave_block_size = txth.interleave;
vgmstream->layout_type = layout_interleave;
}
else {
vgmstream->frame_size = txth.frame_size ? txth.frame_size : txth.interleave;
vgmstream->layout_type = layout_none;
}
vgmstream->allow_dual_stereo = 1; //???
//TO-DO: needs to force MS_IMA_mono first if ch = 1, since dual_stereo + MS_IMA = assumes MS_IMA_stereo
// (or better do it after init / during setup stream)
//vgmstream->allow_dual_stereo = 1;
break;
case coding_MSADPCM:
if (vgmstream->channels > 2) goto fail; //can't handle
if (vgmstream->channels > 2) goto fail; //can't handle (to-do: only non-mono?)
if (txth.interleave && txth.frame_size) {
coding = coding_MSADPCM_int;
vgmstream->frame_size = txth.frame_size;

View File

@ -9,27 +9,28 @@ VGMSTREAM* init_vgmstream_xnb(STREAMFILE* sf) {
STREAMFILE* sf_h = NULL;
off_t start_offset, offset, xma_chunk_offset = 0;
int loop_flag = 0, channel_count, num_samples = 0, loop_start = 0, loop_end = 0;
int big_endian, flags, codec, sample_rate, block_align, bps;
int flags, codec, sample_rate, block_align, bps;
size_t data_size;
char platform;
int is_sound = 0, is_ogg = 0, is_at9 = 0, is_song = 0;
char song_name[255+1];
/* checks */
if ((read_u32be(0x00, sf) & 0xFFFFFF00) != get_id32be("XNB\0"))
goto fail;
return NULL;
if (!check_extensions(sf,"xnb"))
goto fail;
return NULL;
/* XNA Studio platforms: 'w' = Windows, 'm' = Windows Phone 7, 'x' = X360
* MonoGame extensions: 'i' = iOS, 'a' = Android, 'X' = MacOSX, 'P' = PS4, 'S' = Switch, etc */
platform = read_u8(0x03, sf);
big_endian = (platform == 'x');
char platform = read_u8(0x03, sf);
int big_endian = (platform == 'x');
int version = read_u8(0x04,sf);
if (read_u8(0x04,sf) != 0x04 && /* XNA 3.0? found on Scare Me (XBLIG), no notable diffs */
read_u8(0x04,sf) != 0x05) /* XNA 4.0 version */
goto fail;
if (version != 0x03 && /* XNA 2.0? found on Miner: Dig Deep (XBLIG), no notable diffs */
version != 0x04 && /* XNA 3.0? found on Scare Me (XBLIG), no notable diffs */
version != 0x05) /* XNA 4.0 version */
return NULL;
flags = read_u8(0x05, sf);
//if (flags & 0x01) goto fail; /* "HiDef profile" content (no actual difference) */