Merge pull request #958 from bnnm/misc

- Fix some Blitz .str+wav [Zapper (Xbox)]
- Fix some .psb [Castlevania Advance Col. (Switch)]
- misc fixes
This commit is contained in:
bnnm 2021-09-27 00:19:47 +02:00 committed by GitHub
commit fdff2728be
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 666 additions and 473 deletions

View File

@ -552,7 +552,7 @@ class App(object):
if not maker.has_more_subsongs(target_subsong):
break
if target_subsong >= subsong_end:
if subsong_end and target_subsong >= subsong_end:
break
target_subsong += 1

View File

@ -32,9 +32,15 @@
#define APP_INFO APP_NAME " (" __DATE__ ")"
/* low values are ok as there is very little performance difference, but higher
* may improve write I/O in some systems as this*channels doubles as output buffer */
#define SAMPLE_BUFFER_SIZE 32768
/* Low values are ok as there is very little performance difference, but higher
* may improve write I/O in some systems as this*channels doubles as output buffer
* For systems with less memory (like wasm without -s ALLOW_MEMORY_GROWTH) lower helps a bit. */
//TODO: make it selectable with -n? in the future may just use internal bufs for min memory
#ifdef __EMSCRIPTEN__
#define SAMPLE_BUFFER_SIZE 1024
#else
#define SAMPLE_BUFFER_SIZE 32768
#endif
/* getopt globals from .h, for reference (the horror...) */
//extern char* optarg;
@ -150,8 +156,8 @@ static int parse_config(cli_config* cfg, int argc, char** argv) {
cfg->seek_samples1 = -1;
cfg->seek_samples2 = -1;
/* don't let getopt print errors to stdout automatically */
opterr = 0;
opterr = 0; /* don't let getopt print errors to stdout automatically */
optind = 1; /* reset getopt's ugly globals (needed in wasm that may call same main() multiple times) */
/* read config */
while ((opt = getopt(argc, argv, "o:l:f:d:ipPcmxeLEFrgb2:s:t:Tk:K:hOvD:S:"

View File

@ -369,6 +369,15 @@ Follow emscripten's installation instructions:
- https://emscripten.org/docs/getting_started/downloads.html
- https://emscripten.org/docs/compiling/Building-Projects.html#building-projects
Though basically:
```
git clone https://github.com/emscripten-core/emsdk
cd emsdk
./emsdk install latest
./emsdk activate latest
source ./emsdk_env.sh
```
Then you should be able to build it on Linux (Windows would be possible too, but it has some issues at the moment), for example:
```
git clone https://github.com/vgmstream/vgmstream

View File

@ -136,104 +136,68 @@ loop_mode = auto
Note that the number of channels is the sum of all layers so three 2ch layers play as a 6ch file (you can manually downmix using mixing commands, described later, since vgmstream can't guess if the result should be stereo or 5.1 audio).
### Mixed groups
You can set "groups" to 'fold' various files into one, as layers or segments, to allow complex cases. This is an advanced setting for complex cases, so read this after understanding other features first.
### Mixed group mode
You can set "groups" to 'fold' various files into one, as layers or segments, to allow complex mix of segments and layers in the same file. This is a rather advanced setting, so it's explained in detail later (also read other sections first). Most common usage would be:
```
# commands to make two 6ch segments with layered intro + layered loop:
# 1st segment: file of 2ch
# group isn't needed here
bgm1_intro.fsb
# - set introA+B+C as layer (this group becomes position 1, and loopA_2ch position 2)
introA_2ch.at3 #position 1
introB_2ch.at3
introC_2ch.at3
group = 1L3
# 2nd segment: layer 2 files ("L") and keep it 2ch
# equivalent to including a separate .txtp of "mode = layers" with downmixing info (layer-v)
# note that after the internal bgm1_music/vocal aren't "visible" below
bgm1_music.fsb
bgm1_vocal.fsb
group = -L2 #@layer-v
# - set loopA+B+C as layer (this group becomes position 2)
loopA_2ch.at3 #position 4
loopB_2ch.at3
loopC_2ch.at3
group = 2L3
# final result: groups of 2 segments ("S") above
# like making a .txtp of "mode = segments" with 1st .fsb and 2nd .txtp
# b/c "mode = segments" is the default, this last group can be ommited (it's here for clarity)
group = -S2
# - play both as segments (this step is optional if using mode = segments)
group = S2
# loop last segment
loop_mode = auto
# same thing, points to 2nd segment in "final group", but not that clear with groups
#loop_start_segment = 2
```
# - set loop start loopA+B+C (new position 2, not original position 4)
loop_start_segment = 2
```
# group segment of 3, note that in groups you always need to set number of items
bgm2_main_a.fsb
bgm2_main_b.fsb
bgm2_main_c.fsb
group = -S3
# optional, to avoid "segments" default (for debugging)
# another group segment of 3
bgm2_vocal_a.fsb
bgm2_vocal_b.fsb
bgm2_vocal_c.fsb
group = -S3
# final result: layer of 2 groups above
group = -L2 #@layer-v
# optional to avoid "segments" default (for debugging, errors if files don't group correctly)
mode = mixed
```
From TXTP's perspective, it starts with N separate files and every command joins some files to make a single new "file", so positions are reassigned. End result after all grouping will be a single, final "file" that may contain groups within groups. It's pretty flexible so you can express similar things in various ways:
```
# commands to make a 6ch with segmented intro + loop:
introA_2ch.at3
mainA_2ch.at3
# "selectable" (pseudo-random) group, plays 1st internal file only (change to >2 to play 2nd and so on)
bgm2_intro_a.fsb
bgm2_intro_b.fsb
group = -R2>1
introB_2ch.at3
mainB_2ch.at3
# main bgm, also sets this point as loop start
bgm3_main.fsb #@loop
introC_2ch.at3
mainC_2ch.at3
# implicitly loop end
bgm3_outro.fsb
# - group intro/main pairs as segments, starting from 1 and repeating for A/B/C
group = S2R
# - play all as layer (can't set loop_start_segment in this case)
mode = layers
# you could also set: group = L and mode = mixed, same thing
# defaults to segments one of the intros with main bgm
```
### Group definition
`group` can go anywhere in the .txtp, as many times as needed (groups are read and kept in an list that is applied in order at the end). Format is `(position)(type)(count)(repeat)`:
- `position`: file start (optional, default is 1 = first, or set `-` for auto from prev N files)
- `type`: group as `S`=segments, `L`=layers, or `R`=pseudo-random
- `count`: number of files in group (optional, default is all)
- `repeat`: R=repeat group of `count` files until end (optional, default is no repeat)
- `>file`: select file (for pseudo-random groups)
You mix group and groups just like you would mix .txtp with .txtp, resulting in files any combo of single files, layers and segments. Indentation is optional for clarity.
Examples:
- `L`: take all files as layers (equivalent to `mode = layers`)
- `S`: take all files as segments (equivalent to `mode = segments`)
- `3L2`: layer 2 files starting from file 3
- `2L3R`: group every 3 files from position 2 as layers
- `1S1`: segment of one file (mostly useless but allowed as internal file may have play config)
- `1L1`: layer of one file (same)
- `9999L`: absurd values are ignored
`position` may be `-` = automatic, meaning "start from position in previous `count` before current". If `repeat` is set it's ignored though (assumes first).
```
bgm1.adx
bgm2.adx
group = -L2 #layer prev 2 (will start from pos.1 = bgm1, makes group of bgm1+2 = pos.1)
bgm3.adx
bgm4.adx
group = -L2 #layer prev 2 (will start from pos.2 = bgm3, makes group of bgm3+4 = pos.2)
group = -S2 #segment prev 2 (will start from pos.1 = bgm1+2, makes group of bgm1+2 + bgm3+4)
# uses "previous" because "next files" often creates ambiguous cases
# may mix groups of auto and manual positions too, but results are harder to predict
```
### Pseudo-random groups
Group `R` is meant to help with games that randomly select a file in a group. You can set with `>N` which file will be selected. This way you can quickly edit the TXTP and change the file (you could just comment files too, this is just for convenience in complex cases and testing). You can also set `>-`, meaning "play all", basically turning `R` into `S` (this can be omitted, but it's clearer). Files do need to exist and are parsed before being selected, and it can select groups too.
```
bgm1.adx
bgm2.adx
bgm3.adx
group = -R3>1 #first file, change to >2 for second
```
```
bgm1a.adx
bgm1b.adx
group = -S2
bgm2a.adx
bgm2b.adx
group = -S2
group = -R2>2 #select either group >1 or >2
```
### Silent files
You can put `?.` in an entry to make a silent (non-existing) file. By default takes channels and sample rate of nearby files, can be combined with regular commands to configure.
@ -243,7 +207,7 @@ intro.adx
loop.adx
```
It also doubles as a quick "silence this file" while keeping the same structure, for complex cases. The `.` can actually be anywhere after `?`, but must appear before commands to function correctly.
It also doubles as a quick "silence this file" while keeping the same structure, for complex cases. The `.` can actually be anywhere after `?`, but must exists before commands to function correctly (basically, don't silence extension-less files).
```
layer1a.adx
?layer1b.adx
@ -258,36 +222,6 @@ group = -S2
Most of the time you can do the same with `#p`/`#P` padding commands or `#@volume 0.0`. This is mainly for complex engines that combine silent entries in twisted ways. You can't silence `group` with `?group` though since they aren't considered "entries".
### Other considerations
Internally, `mode = segment/layers` are treated basically as a (default, at the end) group. You can apply commands to the resulting group (rather than the individual files) too. `commands` would be applied to this final group.
```
mainA_2ch.at3
mainB_2ch.at3
group = L #h44100
commands = #h48000 #overwrites
```
Segments and layer settings and rules still apply when making groups, so you may need to adjust groups a bit with commands:
```
# this doesn't need to be grouped
intro_2ch.at3
# this is grouped into a single 4ch file, then auto-downmixed to stereo
# (without downmixing may sound a bit strange since channels from mainB wouldn't mix with intro)
mainA_2ch.at3
mainB_2ch.at3
group = -L2 #@layer-v
# finally resulting layers are played as segments (2ch, 2ch)
# (could set a group = S and omit mode here, too)
mode = segments
# if the last group joins all as segments you can use loop_start
loop_start_segment = 3 #refers to final group at position 2
loop_mode = keep
```
Also see loop anchors to handle looping in some cases.
## TXTP COMMANDS
You can set file commands by adding multiple `#(command)` after the name. `#(space)(anything)` is considered a comment and ignored, as well as any command not understood.
@ -867,6 +801,135 @@ To simplify TXTP creation, if the .txtp doesn't set a name inside then its filen
- etc
## GROUPS
Groups can be used to achieve complex .txtp in the same file, but can be a bit hard to understand.
### Group definition
`group` can go anywhere in the .txtp, as many times as needed (groups are read and kept in an list that is applied in order at the end). Format is `(position)(type)(count)(repeat)`:
- `position`: file start (optional, default is 1 = first, or set `-` for 'auto from prev N' files)
- `type`: group as `S`=segments, `L`=layers, or `R`=pseudo-random
- `count`: number of files in group (optional, default is all)
- `repeat`: R=repeat group of `count` files until end (optional, default is no repeat)
- `>file`: select file (for pseudo-random groups)
Examples:
- `L`: take all files as layers (equivalent to `mode = layers`)
- `S`: take all files as segments (equivalent to `mode = segments`)
- `-S2`: segment prev 2 files (start is automatically set)
- `-L2`: layer prev 2 files (start is automatically set)
- `3L2`: layer 2 files starting from file 3
- `2L3R`: group every 3 files from position 2 as layers
- `1S1`: segment of one file (mostly useless but allowed as internal file may have play config)
- `1L1`: layer of one file (same)
- `9999L`: absurd values are ignored
### Usage
`position` may be `-` = automatic, meaning "start from position in previous `count` before current". If `repeat` is set it's ignored though (assumes first).
```
bgm1.adx
bgm2.adx
group = -L2 #layer prev 2 (will start from pos.1 = bgm1, makes group of bgm1+2 = pos.1)
bgm3.adx
bgm4.adx
group = -L2 #layer prev 2 (will start from pos.2 = bgm3, makes group of bgm3+4 = pos.2)
group = -S2 #segment prev 2 (will start from pos.1 = bgm1+2, makes group of bgm1+2 + bgm3+4)
# uses "previous" because "next files" often creates ambiguous cases
```
Usually you want automatic positions, but setting them manually may help understanding what's is going on:
```
# - set introA+B+C as layer (this group becomes position 1, and loopA_2ch position 2)
introA_2ch.at3 #position 1
introB_2ch.at3
introC_2ch.at3
group = 1L3
# - set loopA+B+C as layer (this group becomes position 2)
loopA_2ch.at3 #position 4 > becomes position 2 once prev group is applied
loopB_2ch.at3
loopC_2ch.at3
group = 2L3
# - play both as segments (this step is optional if using mode = segments)
group = S2
# one may mix groups of auto and manual positions too, but results are harder to predict
```
From TXTP's perspective, it starts with N separate files and every command joins some to make a single new "file", so positions are reassigned. End result after all grouping must be a single, final "file" that may contain groups within groups.
That also means you don't need to put groups next to files, if you keep virtual positions in mind. It's pretty flexible so you can express similar things in various ways:
```
introA_2ch.at3
mainA_2ch.at3
introB_2ch.at3
mainB_2ch.at3
introC_2ch.at3
mainC_2ch.at3
# - group intro/main pairs as segments, starting from 1 and repeating for A/B/C
# same as writting "group = -S2" x3
group = S2R
# - play all as layer (can't set loop_start_segment in this case)
# you could also set: group = L and mode = mixed, same thing
mode = layers
```
### Pseudo-random groups
Group `R` is meant to help with games that randomly select a file in a group (like some intro or outro). You can set with `>N` which file will be selected. This way you can quickly edit the TXTP and change the file (you could just comment files too, this is just for convenience in complex cases and testing). You can also set `>-`, meaning "play all", basically turning `R` into `S` (this can be omitted, but it's clearer). Files do need to exist and are parsed before being selected, and it can select groups too.
```
bgm1.adx
bgm2.adx
bgm3.adx
group = -R3>1 #first file, change to >2 for second
```
```
bgm1a.adx
bgm1b.adx
group = -S2
bgm2a.adx
bgm2b.adx
group = -S2
group = -R2>2 #select either group >1 or >2
```
### Other considerations
Internally, `mode = segment/layers` are treated basically as a (default, at the end) group. You can apply commands to the resulting group (rather than the individual files) too. `commands` would be applied to this final group.
```
mainA_2ch.at3
mainB_2ch.at3
group = L #h44100
commands = #h48000 #overwrites
```
Segments and layer settings and rules still apply when making groups, so you may need to adjust groups a bit with commands:
```
# this doesn't need to be grouped
intro_2ch.at3
# this is grouped into a single 4ch file, then auto-downmixed to stereo
# (without downmixing may sound a bit strange since channels from mainB wouldn't mix with intro)
mainA_2ch.at3
mainB_2ch.at3
group = -L2 #@layer-v
# finally resulting layers are played as segments (2ch, 2ch)
# (could set a group = S and omit mode here, too)
mode = segments
# if the last group joins all as segments you can use loop_start
loop_start_segment = 3 #refers to final group at position 2
loop_mode = keep
```
Also see loop anchors to handle looping in some cases.
## MIXING
Sometimes games use multiple channels in uncommon ways, for example as layered tracks for dynamic music (like main+vocals), or crossfading a stereo song to another stereo song. In those cases we normally would want a stereo track, but vgmstream can't guess how channels are used (since it's game-dependent). To solve this via TXTP you can set mixing output and volumes manually.

View File

@ -323,7 +323,8 @@ name (e.g. `STREAM.SS0`), but sometimes the actual filename is in other case
You could try adding *symlinks* in various upper/lower/mixed cases to handle this,
though only a few formats do this, mainly *Ubisoft* banks.
Regular formats without companion files should work fine in upper/lowercase.
Regular formats without companion files should work fine in upper/lowercase. For
`.(ext).txth` files make sure `(ext)` matches case too.
### Decryption keys
Certain formats have encrypted data, and need a key to decrypt. vgmstream
@ -477,6 +478,26 @@ You can also choose which channels to play using *TXTP*. For example, create
a file named `song.adx#C1,2.txtp` to play only channels 1 and 2 from `song.adx`.
*TXTP* also has command to set how files are downmixed.
### Average bitrate
Note that vgmstream shows the "file bitrate" (counts all data) as opposed to
"codec bitrate" (counts pure audio-only parts). This means bitrate may be
slightly higher (or much higher, if file is bloated) than what encoder
tools or other players may report.
Calculating 100% correct codec bitrate usually needs manual reading of the whole
file, slowing down opening files and needing extra effort by devs for minimal
benefit, so it's not done.
In some cases it's debatable what the codec bitrate is. Unlike MP3/AAC, 48kbps
of raw Vorbis/Opus is unplayable/unusable unless it's packed into .ogg/wem/etc
with extra data, that does increase final file size (thus bitrate) by some percent.
Also, keep in mind video game audio bitrate isn't always a great indicator of quality.
There are many factors in play like encoder, type of codec, sample rate and so on.
A higher bitrate `.wav` can sound worse than a lower `.ogg` (like mono 22050hz `.wav`
vs stereo 48000hz `.ogg`).
## Logged errors and unplayable supported files
Some formats should normally play, but somehow don't. In those cases plugins
can print vgmstream's error info to console (for example, `.fsb` with an unknown

View File

@ -176,6 +176,8 @@ function Clean
Remove-Item -Path "Release" -Recurse -ErrorAction Ignore
Remove-Item -Path "bin" -Recurse -ErrorAction Ignore
Remove-Item -Path "tmp" -Recurse -ErrorAction Ignore
Remove-Item "msvc-build.log" -ErrorAction Ignore
}
$fb2kFiles = @(
@ -207,7 +209,7 @@ $cliPdbFiles = @(
"$configuration/xmp-vgmstream.pdb"
)
function Package
function MakePackage
{
Build
@ -229,9 +231,9 @@ function Package
# for github actions/artifact uploads, that use a dir with files
function PackageTmp
function MakePackageTmp
{
Package
MakePackage
md -Force tmp/cli
md -Force tmp/fb2k
@ -251,6 +253,6 @@ switch ($Task)
"Build" { Build }
"Rebuild" { Rebuild }
"Clean" { Clean }
"Package" { Package }
"PackageTmp" { PackageTmp }
"Package" { MakePackage }
"PackageTmp" { MakePackageTmp }
}

View File

@ -680,7 +680,7 @@ size_t atrac3plus_bytes_to_samples(size_t bytes, int full_block_align);
size_t ac3_bytes_to_samples(size_t bytes, int full_block_align, int channels);
size_t aac_get_samples(STREAMFILE* sf, off_t start_offset, size_t bytes);
size_t mpeg_get_samples(STREAMFILE* sf, off_t start_offset, size_t bytes);
int32_t mpeg_get_samples_clean(STREAMFILE* sf, off_t start, size_t size, size_t* p_loop_start, size_t* p_loop_end, int is_vbr);
int32_t mpeg_get_samples_clean(STREAMFILE* sf, off_t start, size_t size, uint32_t* p_loop_start, uint32_t* p_loop_end, int is_vbr);
int mpc_get_samples(STREAMFILE* sf, off_t offset, int32_t* p_samples, int32_t* p_delay);

View File

@ -55,7 +55,7 @@ struct ffmpeg_codec_data {
};
#define FFMPEG_DEFAULT_IO_BUFFER_SIZE 128 * 1024
#define FFMPEG_DEFAULT_IO_BUFFER_SIZE STREAMFILE_DEFAULT_BUFFER_SIZE
static volatile int g_ffmpeg_initialized = 0;
@ -322,7 +322,7 @@ ffmpeg_codec_data* init_ffmpeg_header_offset_subsong(STREAMFILE* sf, uint8_t* he
goto fail;
if (size == 0 || start + size > get_streamfile_size(sf)) {
vgm_asserti(size != 0, "FFMPEG: wrong start+size found: %x + %x > %x \n", (uint32_t)start, (uint32_t)size, get_streamfile_size(sf));
vgm_asserti(size != 0, "FFMPEG: wrong start+size found: %x + %x > %x \n", (uint32_t)start, (uint32_t)size, (uint32_t)get_streamfile_size(sf));
size = get_streamfile_size(sf) - start;
}

View File

@ -451,7 +451,7 @@ size_t mpeg_get_samples(STREAMFILE* sf, off_t start_offset, size_t bytes) {
/* variation of the above, for clean streams = no ID3/VBR headers
* (maybe should be fused in a single thing with config, API is kinda messy too) */
int32_t mpeg_get_samples_clean(STREAMFILE *sf, off_t start, size_t size, size_t* p_loop_start, size_t* p_loop_end, int is_vbr) {
int32_t mpeg_get_samples_clean(STREAMFILE* sf, off_t start, size_t size, uint32_t* p_loop_start, uint32_t* p_loop_end, int is_vbr) {
mpeg_frame_info info;
off_t offset = start;
int32_t num_samples = 0, loop_start = 0, loop_end = 0;

View File

@ -1,9 +1,9 @@
#include "vorbis_custom_decoder.h"
#ifdef VGM_USE_VORBIS
#define BITSTREAM_READ_ONLY /* config */
#include "vorbis_bitreader.h"
#ifdef VGM_USE_VORBIS
#include <vorbis/codec.h>

View File

@ -1,7 +1,7 @@
#include "vorbis_custom_decoder.h"
#include "vorbis_bitreader.h"
#ifdef VGM_USE_VORBIS
#include "vorbis_bitreader.h"
#include <vorbis/codec.h>
#define WWISE_VORBIS_USE_PRECOMPILED_WVC 1 /* if enabled vgmstream weights ~150kb more but doesn't need external .wvc packets */

View File

@ -2,18 +2,17 @@
#include "../coding/coding.h"
/* .AO - from AlphaOgg lib [Cloudphobia (PC), GEO ~The Sword Millennia~ Kasumi no Tani no Kaibutsu (PC)] */
VGMSTREAM* init_vgmstream_ao(STREAMFILE *sf) {
VGMSTREAM* init_vgmstream_ao(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
off_t start_offset;
/* checks */
if (!check_extensions(sf,"ao"))
goto fail;
if (!is_id64be(0x00,sf, "ALPHAOGG"))
goto fail;
if (!check_extensions(sf,"ao"))
goto fail;
#ifdef VGM_USE_VORBIS
{
ogg_vorbis_meta_info_t ovmi = {0};
int sample_rate = read_u32le(0xF0, sf); /* Ogg header */
@ -29,9 +28,6 @@ VGMSTREAM* init_vgmstream_ao(STREAMFILE *sf) {
start_offset = 0xc8;
vgmstream = init_vgmstream_ogg_vorbis_config(sf, start_offset, &ovmi);
}
#else
goto fail;
#endif
return vgmstream;
fail:

View File

@ -10,7 +10,7 @@ VGMSTREAM* init_vgmstream_atsl(STREAMFILE* sf) {
int type, big_endian = 0, entries;
uint32_t subfile_offset = 0, subfile_size = 0, header_size, entry_size;
VGMSTREAM* (*init_vgmstream)(STREAMFILE* sf) = NULL;
init_vgmstream_t init_vgmstream = NULL;
const char* fake_ext;
@ -47,7 +47,6 @@ VGMSTREAM* init_vgmstream_atsl(STREAMFILE* sf) {
type = read_u16le(0x0c, sf);
switch(type) {
#ifdef VGM_USE_VORBIS
case 0x0100: /* KOVS */
init_vgmstream = init_vgmstream_ogg_vorbis;
fake_ext = "kvs";
@ -58,7 +57,6 @@ VGMSTREAM* init_vgmstream_atsl(STREAMFILE* sf) {
fake_ext = "kvs";
entry_size = 0x3c;
break;
#endif
case 0x0200: /* ATRAC3 */
init_vgmstream = init_vgmstream_riff;
fake_ext = "at3";

View File

@ -38,9 +38,8 @@ VGMSTREAM* init_vgmstream_encrypted(STREAMFILE* sf) {
temp_sf = setup_ogg_vorbis_streamfile(sf, cfg);
if (!temp_sf) goto fail;
#ifdef VGM_USE_VORBIS
vgmstream = init_vgmstream_ogg_vorbis(temp_sf);
#endif
close_streamfile(temp_sf);
return vgmstream;
}

View File

@ -1,97 +1,96 @@
#include "meta.h"
#include "../coding/coding.h"
/* FFDL - Matrix Software wrapper [Final Fantasy Dimensions (Android/iOS)] */
VGMSTREAM * init_vgmstream_ffdl(STREAMFILE *sf) {
VGMSTREAM * vgmstream = NULL;
STREAMFILE *temp_sf = NULL;
int loop_flag = 0, is_ffdl = 0;
int32_t num_samples = 0, loop_start_sample = 0, loop_end_sample = 0;
off_t start_offset;
size_t file_size;
/* checks */
/* .ogg/logg: probable extension for Android
* .mp4/lmp4: probable extension for iOS
* .bin: iOS FFDL extension
* (extensionless): for FFDL files without names in Android .obb bigfile */
if (!check_extensions(sf, "ogg,logg,mp4,lmp4,bin,"))
goto fail;
/* "FFDL" is a wrapper used in all of the game's files, that may contain standard
* Ogg/MP4 or "mtxs" w/ loops + Ogg/MP4, and may concatenate multiple of them
* (without size in sight), so they should be split externally first. */
start_offset = 0x00;
/* may start with wrapper (not split) */
if (read_u32be(0x00,sf) == 0x4646444C) { /* "FFDL" */
is_ffdl = 1;
start_offset += 0x04;
}
/* may start with sample info (split) or after "FFDL" */
if (read_u32be(start_offset+0x00,sf) == 0x6D747873) { /* "mtxs" */
is_ffdl = 1;
num_samples = read_s32le(start_offset + 0x04,sf);
loop_start_sample = read_s32le(start_offset + 0x08,sf);
loop_end_sample = read_s32le(start_offset + 0x0c,sf);
loop_flag = !(loop_start_sample==0 && loop_end_sample==num_samples);
start_offset += 0x10;
}
/* don't parse regular files */
if (!is_ffdl)
goto fail;
file_size = get_streamfile_size(sf) - start_offset;
if (read_u32be(start_offset + 0x00,sf) == 0x4F676753) { /* "OggS" */
#ifdef VGM_USE_VORBIS
temp_sf = setup_subfile_streamfile(sf, start_offset, file_size, "ogg");
if (!temp_sf) goto fail;
vgmstream = init_vgmstream_ogg_vorbis(temp_sf);
if (!vgmstream) goto fail;
#else
goto fail;
#endif
}
else if (read_u32be(start_offset + 0x04,sf) == 0x66747970) { /* "ftyp" after atom size */
#ifdef VGM_USE_FFMPEG
temp_sf = setup_subfile_streamfile(sf, start_offset, file_size, "mp4");
if (!temp_sf) goto fail;
vgmstream = init_vgmstream_mp4_aac_ffmpeg(temp_sf);
if (!vgmstream) goto fail;
#else
goto fail;
#endif
}
else {
goto fail;
}
/* install loops */
if (loop_flag) {
/* num_samples is erratic (can be bigger = padded, or smaller = cut; doesn't matter for looping though) */
//;VGM_ASSERT(vgmstream->num_samples != num_samples,
// "FFDL: mtxs samples = %i vs num_samples = %i\n", num_samples, vgmstream->num_samples);
//vgmstream->num_samples = num_samples;
/* loop samples are within num_samples, and don't have encoder delay (loop_start=0 starts from encoder_delay) */
vgmstream_force_loop(vgmstream, 1, loop_start_sample, loop_end_sample);
}
close_streamfile(temp_sf);
return vgmstream;
fail:
close_streamfile(temp_sf);
close_vgmstream(vgmstream);
return NULL;
}
#include "meta.h"
#include "../coding/coding.h"
/* FFDL - Matrix Software wrapper [Final Fantasy Dimensions (Android/iOS)] */
VGMSTREAM* init_vgmstream_ffdl(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
STREAMFILE* temp_sf = NULL;
int loop_flag = 0, is_ffdl = 0;
int32_t num_samples = 0, loop_start_sample = 0, loop_end_sample = 0;
off_t start_offset;
size_t file_size;
/* checks */
if (!is_id32be(0x00,sf, "FFDL") &&
!is_id32be(0x00,sf, "mtxs"))
goto fail;
/* .ogg/logg: probable extension for Android
* .mp4/lmp4: probable extension for iOS
* .bin: iOS FFDL extension
* (extensionless): for FFDL files without names in Android .obb bigfile */
if (!check_extensions(sf, "ogg,logg,mp4,lmp4,bin,"))
goto fail;
/* "FFDL" is a wrapper used in all of the game's files, that may contain standard
* Ogg/MP4 or "mtxs" w/ loops + Ogg/MP4, and may concatenate multiple of them
* (without size in sight), so they should be split externally first. */
/* may start with wrapper (not split) */
start_offset = 0x00;
if (is_id32be(0x00,sf, "FFDL")) {
is_ffdl = 1;
start_offset += 0x04;
}
/* may start with sample info (split) or after "FFDL" */
if (is_id32be(start_offset+0x00,sf, "mtxs")) {
is_ffdl = 1;
num_samples = read_s32le(start_offset + 0x04,sf);
loop_start_sample = read_s32le(start_offset + 0x08,sf);
loop_end_sample = read_s32le(start_offset + 0x0c,sf);
loop_flag = !(loop_start_sample==0 && loop_end_sample==num_samples);
start_offset += 0x10;
}
/* don't parse regular files */
if (!is_ffdl)
goto fail;
file_size = get_streamfile_size(sf) - start_offset;
if (read_u32be(start_offset + 0x00,sf) == 0x4F676753) { /* "OggS" */
temp_sf = setup_subfile_streamfile(sf, start_offset, file_size, "ogg");
if (!temp_sf) goto fail;
vgmstream = init_vgmstream_ogg_vorbis(temp_sf);
if (!vgmstream) goto fail;
}
else if (read_u32be(start_offset + 0x04,sf) == 0x66747970) { /* "ftyp" after atom size */
#ifdef VGM_USE_FFMPEG
temp_sf = setup_subfile_streamfile(sf, start_offset, file_size, "mp4");
if (!temp_sf) goto fail;
vgmstream = init_vgmstream_mp4_aac_ffmpeg(temp_sf);
if (!vgmstream) goto fail;
#else
goto fail;
#endif
}
else {
goto fail;
}
/* install loops */
if (loop_flag) {
/* num_samples is erratic (can be bigger = padded, or smaller = cut; doesn't matter for looping though) */
//;VGM_ASSERT(vgmstream->num_samples != num_samples,
// "FFDL: mtxs samples = %i vs num_samples = %i\n", num_samples, vgmstream->num_samples);
//vgmstream->num_samples = num_samples;
/* loop samples are within num_samples, and don't have encoder delay (loop_start=0 starts from encoder_delay) */
vgmstream_force_loop(vgmstream, 1, loop_start_sample, loop_end_sample);
}
close_streamfile(temp_sf);
return vgmstream;
fail:
close_streamfile(temp_sf);
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -18,10 +18,10 @@ typedef struct {
int32_t loop_end;
int loop_flag;
size_t sample_header_size;
size_t name_table_size;
size_t sample_data_size;
size_t base_header_size;
uint32_t sample_header_size;
uint32_t name_table_size;
uint32_t sample_data_size;
uint32_t base_header_size;
uint32_t extradata_offset;
uint32_t extradata_size;
@ -46,15 +46,15 @@ VGMSTREAM* init_vgmstream_fsb5(STREAMFILE* sf) {
/* checks */
if (!is_id32be(0x00,sf, "FSB5"))
goto fail;
/* .fsb: standard
* .snd: Alchemy engine (also Unity) */
if (!check_extensions(sf,"fsb,snd"))
goto fail;
if (!is_id32be(0x00,sf, "FSB5"))
goto fail;
/* v0 is rare (seen in Tales from Space Vita) */
/* v0 is rare, seen in Tales from Space (Vita) */
fsb5.version = read_u32le(0x04,sf);
if (fsb5.version != 0x00 && fsb5.version != 0x01)
goto fail;
@ -80,7 +80,7 @@ VGMSTREAM* init_vgmstream_fsb5(STREAMFILE* sf) {
}
if ((fsb5.sample_header_size + fsb5.name_table_size + fsb5.sample_data_size + fsb5.base_header_size) != get_streamfile_size(sf)) {
vgm_logi("FSB5: wrong size, expected %x + %x + %x + %x vs %x (re-rip)\n", fsb5.sample_header_size, fsb5.name_table_size, fsb5.sample_data_size, fsb5.base_header_size, get_streamfile_size(sf));
vgm_logi("FSB5: wrong size, expected %x + %x + %x + %x vs %x (re-rip)\n", fsb5.sample_header_size, fsb5.name_table_size, fsb5.sample_data_size, fsb5.base_header_size, (uint32_t)get_streamfile_size(sf));
goto fail;
}

View File

@ -5,47 +5,50 @@
/* HIS - Her Interactive games [Nancy Drew series (PC)] */
VGMSTREAM * init_vgmstream_his(STREAMFILE *sf) {
VGMSTREAM * vgmstream = NULL;
int channel_count, loop_flag = 0, bps, sample_rate, num_samples, version;
int channels, loop_flag = 0, bps, sample_rate, num_samples, version;
off_t start_offset;
/* checks */
if (!is_id32be(0x00,sf, "Her ") &&
!is_id32be(0x00,sf, "HIS\0"))
goto fail;
if (!check_extensions(sf, "his"))
goto fail;
if (read_32bitBE(0x00,sf) == 0x48657220) { /* "Her Interactive Sound\x1a" */
if (is_id32be(0x00,sf, "Her ")) { /* "Her Interactive Sound\x1a" */
/* Nancy Drew: Secrets Can Kill (PC) */
version = 0;
channel_count = read_16bitLE(0x16,sf);
sample_rate = read_32bitLE(0x18,sf);
channels = read_u16le(0x16,sf);
sample_rate = read_u32le(0x18,sf);
/* 0x1c: bitrate */
/* 0x20: block size */
bps = read_16bitLE(0x22,sf);
bps = read_u16le(0x22,sf);
if (read_32bitBE(0x24,sf) != 0x64617461) /* "data" */
if (!is_id32be(0x24,sf, "data"))
goto fail;
num_samples = pcm_bytes_to_samples(read_32bitLE(0x28,sf), channel_count, bps);
num_samples = pcm_bytes_to_samples(read_u32le(0x28,sf), channels, bps);
start_offset = 0x2c;
}
else if (read_32bitBE(0x00,sf) == 0x48495300) { /* HIS\0 */
else if (is_id32be(0x00,sf, "HIS\0")) {
/* most(?) others */
version = read_32bitLE(0x04,sf);
version = read_u32le(0x04,sf);
/* 0x08: codec */
channel_count = read_16bitLE(0x0a,sf);
sample_rate = read_32bitLE(0x0c,sf);
channels = read_u16le(0x0a,sf);
sample_rate = read_u32le(0x0c,sf);
/* 0x10: bitrate */
/* 0x14: block size */
bps = read_16bitLE(0x16,sf);
bps = read_u16le(0x16,sf);
num_samples = pcm_bytes_to_samples(read_32bitLE(0x18,sf), channel_count, bps); /* true even for Ogg */
num_samples = pcm_bytes_to_samples(read_u32le(0x18,sf), channels, bps); /* true even for Ogg */
/* later games use "OggS" */
if (version == 1)
start_offset = 0x1c; /* Nancy Drew: The Final Scene (PC) */
else if (version == 2 && read_32bitBE(0x1e,sf) == 0x4F676753)
else if (version == 2 && is_id32be(0x1e,sf, "OggS"))
start_offset = 0x1e; /* Nancy Drew: The Haunted Carousel (PC) */
else if (version == 2 && read_32bitBE(0x20,sf) == 0x4F676753)
else if (version == 2 && is_id32be(0x20,sf, "OggS"))
start_offset = 0x20; /* Nancy Drew: The Silent Spy (PC) */
else
goto fail;
@ -56,18 +59,14 @@ VGMSTREAM * init_vgmstream_his(STREAMFILE *sf) {
if (version == 2) {
#ifdef VGM_USE_VORBIS
ogg_vorbis_meta_info_t ovmi = {0};
ovmi.meta_type = meta_HIS;
return init_vgmstream_ogg_vorbis_config(sf, start_offset, &ovmi);
#else
goto fail;
#endif
}
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_HIS;
@ -89,7 +88,7 @@ VGMSTREAM * init_vgmstream_his(STREAMFILE *sf) {
goto fail;
}
if (!vgmstream_open_stream(vgmstream,sf,start_offset))
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
goto fail;
return vgmstream;

View File

@ -10,14 +10,14 @@ VGMSTREAM* init_vgmstream_ikm_ps2(STREAMFILE* sf) {
/* checks */
if ( !check_extensions(sf,"ikm") )
if (!is_id32be(0x00,sf, "IKM\0"))
goto fail;
if (read_u32be(0x00,sf) != 0x494B4D00) /* "IKM\0" */
if (!check_extensions(sf,"ikm"))
goto fail;
if (read_u32be(0x40,sf) != 0x41535400) /* "AST\0" */
goto fail;
/* 0x20: type 03? */
if (!is_id32be(0x40,sf, "AST\0"))
goto fail;
loop_flag = (read_s32le(0x14, sf) > 0);
channel_count = read_s32le(0x50, sf);
@ -53,23 +53,24 @@ VGMSTREAM* init_vgmstream_ikm_pc(STREAMFILE* sf) {
/* checks */
if ( !check_extensions(sf,"ikm") )
if (!is_id32be(0x00,sf, "IKM\0"))
goto fail;
if (read_u32be(0x00,sf) != 0x494B4D00) /* "IKM\0" */
if (!check_extensions(sf,"ikm"))
goto fail;
/* 0x20: type 01? */
/* find "OggS" start */
if (read_u32be(0x30,sf) == 0x4F676753) {
if (is_id32be(0x30,sf, "OggS")) {
start_offset = 0x30; /* Chaos Legion (PC) */
} else if (read_u32be(0x800,sf) == 0x4F676753) {
}
else if (is_id32be(0x800,sf, "OggS")) {
start_offset = 0x800; /* Legend of Galactic Heroes (PC) */
} else {
}
else {
goto fail;
}
#ifdef VGM_USE_VORBIS
{
ogg_vorbis_meta_info_t ovmi = {0};
@ -82,9 +83,6 @@ VGMSTREAM* init_vgmstream_ikm_pc(STREAMFILE* sf) {
vgmstream = init_vgmstream_ogg_vorbis_config(sf, start_offset, &ovmi);
}
#else
goto fail;
#endif
return vgmstream;
@ -102,13 +100,14 @@ VGMSTREAM* init_vgmstream_ikm_psp(STREAMFILE* sf) {
/* checks */
if (!is_id32be(0x00,sf, "IKM\0"))
goto fail;
if (!check_extensions(sf,"ikm"))
goto fail;
if (read_u32be(0x00,sf) != 0x494B4D00) /* "IKM\0" */
goto fail;
if (read_u32be(0x800,sf) != 0x52494646) /* "RIFF" */
goto fail;
/* 0x20: type 00? */
if (!is_id32be(0x800,sf, "RIFF"))
goto fail;
/* loop values (pre-adjusted without encoder delay) at 0x14/18 are found in the RIFF too */
data_size = read_s32le(0x24, sf);

View File

@ -3,6 +3,8 @@
#include "../vgmstream.h"
typedef VGMSTREAM* (*init_vgmstream_t)(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_silence(int channels, int sample_rate, int32_t num_samples);
VGMSTREAM* init_vgmstream_silence_container(int total_subsongs);
@ -116,8 +118,7 @@ VGMSTREAM * init_vgmstream_vpk(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile);
#ifdef VGM_USE_VORBIS
VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile);
VGMSTREAM* init_vgmstream_ogg_vorbis(STREAMFILE* sf);
typedef struct {
int loop_flag;
@ -142,16 +143,15 @@ typedef struct {
} ogg_vorbis_meta_info_t;
VGMSTREAM* init_vgmstream_ogg_vorbis_config(STREAMFILE *sf, off_t start, const ogg_vorbis_meta_info_t* ovmi);
#endif
VGMSTREAM* init_vgmstream_ogg_vorbis_config(STREAMFILE* sf, off_t start, const ogg_vorbis_meta_info_t* ovmi);
VGMSTREAM * init_vgmstream_hca(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_hca_subkey(STREAMFILE *streamFile, uint16_t subkey);
VGMSTREAM* init_vgmstream_hca(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_hca_subkey(STREAMFILE* sf, uint16_t subkey);
#ifdef VGM_USE_FFMPEG
VGMSTREAM * init_vgmstream_ffmpeg(STREAMFILE *streamFile);
VGMSTREAM* init_vgmstream_ffmpeg(STREAMFILE* sf);
VGMSTREAM * init_vgmstream_mp4_aac_ffmpeg(STREAMFILE * streamFile);
VGMSTREAM* init_vgmstream_mp4_aac_ffmpeg(STREAMFILE* sf);
#endif
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)

View File

@ -1,39 +1,27 @@
/*
2017-12-10: Preliminary MOGG Support. As long as the stream is unencrypted, this should be fine.
This will also work on unconventional 5 channel Vorbis streams but some sound cards might not like it.
TODO (Eventually): Add decryption for encrypted MOGG types (Rock Band, etc.)
-bxaimc
*/
#include "meta.h"
#include "../coding/coding.h"
/* MOGG - Harmonix Music Systems (Guitar Hero)[Unencrypted Type] */
VGMSTREAM* init_vgmstream_mogg(STREAMFILE *sf) {
#ifdef VGM_USE_VORBIS
/* MOGG - Harmonix Music Systems's Ogg (unencrypted type) [Guitar Hero II (X360)] */
VGMSTREAM* init_vgmstream_mogg(STREAMFILE* sf) {
off_t start_offset;
/* checks */
if (read_u32le(0x00, sf) != 0x0A) /* type? */
goto fail;
if (!check_extensions(sf, "mogg"))
goto fail;
{
ogg_vorbis_meta_info_t ovmi = {0};
VGMSTREAM * result = NULL;
ovmi.meta_type = meta_MOGG;
start_offset = read_32bitLE(0x04, sf);
result = init_vgmstream_ogg_vorbis_config(sf, start_offset, &ovmi);
if (result != NULL) {
return result;
}
start_offset = read_u32le(0x04, sf);
return init_vgmstream_ogg_vorbis_config(sf, start_offset, &ovmi);
}
fail:
/* clean up anything we may have opened */
#endif
return NULL;
}

View File

@ -6,30 +6,27 @@
/* MUPS - from Watermelon/HUCARD games (same programmer) [Pier Solar and the Great Architects (PC), Ghost Blade HD (PC/Switch)] */
VGMSTREAM* init_vgmstream_mups(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
STREAMFILE *temp_sf = NULL;
STREAMFILE* temp_sf = NULL;
/* checks */
if (!is_id32be(0x00,sf, "MUPS"))
goto fail;
/* mups: header id?
* (extensionless): default? */
if (!check_extensions(sf, "mups,"))
goto fail;
if (read_u32be(0x00,sf) != 0x4D555053) /* "MUPS" */
goto fail;
if (read_u32be(0x08,sf) != 0x50737348) /* "PssH" */
if (!is_id32be(0x08,sf, "PssH"))
goto fail;
/* just an Ogg with changed OggS/vorbis words (see streamfile) */
temp_sf = setup_mups_streamfile(sf, 0x08);
if (!temp_sf) goto fail;
#ifdef VGM_USE_VORBIS
vgmstream = init_vgmstream_ogg_vorbis(temp_sf);
if (!vgmstream) goto fail;
#else
goto fail;
#endif
close_streamfile(temp_sf);

View File

@ -11,11 +11,11 @@
#endif
static char** parse_mus(STREAMFILE *streamFile, int *out_file_count, int *out_loop_flag, int *out_loop_start_index, int *out_loop_end_index);
static char** parse_mus(STREAMFILE* sf, int *out_file_count, int *out_loop_flag, int *out_loop_start_index, int *out_loop_end_index);
static void clean_mus(char** mus_filenames, int file_count);
/* .MUS - playlist for InterPlay games [Planescape: Torment (PC), Baldur's Gate Enhanced Edition (PC)] */
VGMSTREAM * init_vgmstream_mus_acm(STREAMFILE *streamFile) {
VGMSTREAM* init_vgmstream_mus_acm(STREAMFILE* sf) {
VGMSTREAM * vgmstream = NULL;
segmented_layout_data *data = NULL;
@ -27,11 +27,11 @@ VGMSTREAM * init_vgmstream_mus_acm(STREAMFILE *streamFile) {
/* checks */
if (!check_extensions(streamFile, "mus"))
if (!check_extensions(sf, "mus"))
goto fail;
/* get file paths from the .MUS text file */
mus_filenames = parse_mus(streamFile, &segment_count, &loop_flag, &loop_start_index, &loop_end_index);
mus_filenames = parse_mus(sf, &segment_count, &loop_flag, &loop_start_index, &loop_end_index);
if (!mus_filenames) goto fail;
/* init layout */
@ -40,24 +40,22 @@ VGMSTREAM * init_vgmstream_mus_acm(STREAMFILE *streamFile) {
/* open each segment subfile */
for (i = 0; i < segment_count; i++) {
STREAMFILE* temp_streamFile = streamFile->open(streamFile, mus_filenames[i], STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!temp_streamFile) goto fail;
STREAMFILE* temp_sf = sf->open(sf, mus_filenames[i], STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!temp_sf) goto fail;
/* find .ACM type */
switch(read_32bitBE(0x00,temp_streamFile)) {
switch(read_32bitBE(0x00,temp_sf)) {
case 0x97280301: /* ACM header id [Planescape: Torment (PC)] */
data->segments[i] = init_vgmstream_acm(temp_streamFile);
data->segments[i] = init_vgmstream_acm(temp_sf);
break;
#ifdef VGM_USE_VORBIS
case 0x4F676753: /* "OggS" [Planescape: Torment Enhanced Edition (PC)] */
data->segments[i] = init_vgmstream_ogg_vorbis(temp_streamFile);
data->segments[i] = init_vgmstream_ogg_vorbis(temp_sf);
break;
#endif
default:
data->segments[i] = NULL;
break;
}
close_streamfile(temp_streamFile);
close_streamfile(temp_sf);
if (!data->segments[i]) goto fail;
@ -184,7 +182,7 @@ fail:
return 1;
}
static char** parse_mus(STREAMFILE *streamFile, int *out_file_count, int *out_loop_flag, int *out_loop_start_index, int *out_loop_end_index) {
static char** parse_mus(STREAMFILE *sf, int *out_file_count, int *out_loop_flag, int *out_loop_start_index, int *out_loop_end_index) {
char** names = NULL;
char filename[NAME_LENGTH];
@ -204,7 +202,7 @@ static char** parse_mus(STREAMFILE *streamFile, int *out_file_count, int *out_lo
/* read file name base */
bytes_read = read_line(line, sizeof(line), mus_offset, streamFile, &line_ok);
bytes_read = read_line(line, sizeof(line), mus_offset, sf, &line_ok);
if (!line_ok) goto fail;
mus_offset += bytes_read;
memcpy(name_base,line,sizeof(name_base));
@ -217,7 +215,7 @@ static char** parse_mus(STREAMFILE *streamFile, int *out_file_count, int *out_lo
}
/* read track entry count */
bytes_read = read_line(line, sizeof(line), mus_offset, streamFile, &line_ok);
bytes_read = read_line(line, sizeof(line), mus_offset, sf, &line_ok);
if (!line_ok) goto fail;
if (line[0] == '\0') goto fail;
mus_offset += bytes_read;
@ -235,7 +233,7 @@ static char** parse_mus(STREAMFILE *streamFile, int *out_file_count, int *out_lo
}
dir_name[0]='\0';
streamFile->get_name(streamFile,filename,sizeof(filename));
sf->get_name(sf,filename,sizeof(filename));
concatn(sizeof(dir_name),dir_name,filename);
/* find directory name for the directory contianing the MUS */
@ -262,7 +260,7 @@ static char** parse_mus(STREAMFILE *streamFile, int *out_file_count, int *out_lo
for (i = 0; i < file_count; i++)
{
int fields_matched;
bytes_read = read_line(line,sizeof(line), mus_offset, streamFile, &line_ok);
bytes_read = read_line(line,sizeof(line), mus_offset, sf, &line_ok);
if (!line_ok) goto fail;
mus_offset += bytes_read;
@ -308,13 +306,13 @@ static char** parse_mus(STREAMFILE *streamFile, int *out_file_count, int *out_lo
concatn(NAME_LENGTH,names[i],name);
concatn(NAME_LENGTH,names[i],".ACM");
if (!exists(names[i],streamFile)) {
if (!exists(names[i],sf)) {
/* We can't test for the directory until we have a file name
* to look for, so we do it here with the first file that seems to
* be in a subdirectory */
if (subdir_name[0]=='\0') {
if (find_directory_name(name_base, dir_name, sizeof(subdir_name), subdir_name, name, filename, streamFile))
if (find_directory_name(name_base, dir_name, sizeof(subdir_name), subdir_name, name, filename, sf))
goto fail;
}
@ -325,7 +323,7 @@ static char** parse_mus(STREAMFILE *streamFile, int *out_file_count, int *out_lo
concatn(NAME_LENGTH,names[i],name);
concatn(NAME_LENGTH,names[i],".ACM");
if (!exists(names[i],streamFile)) goto fail;
if (!exists(names[i],sf)) goto fail;
}
}

View File

@ -2,37 +2,35 @@
#include "../coding/coding.h"
/* NWAV - from Chunsoft games [Fuurai no Shiren Gaiden: Onnakenshi Asuka Kenzan! (PC)] */
VGMSTREAM * init_vgmstream_nwav(STREAMFILE *sf) {
VGMSTREAM * vgmstream = NULL;
VGMSTREAM* init_vgmstream_nwav(STREAMFILE* sf) {
off_t start_offset;
/* checks */
if (!is_id32be(0x00,sf, "NWAV"))
goto fail;
/* .nwav: header id (no filenames in bigfiles) */
if ( !check_extensions(sf,"nwav") )
goto fail;
if (read_32bitBE(0x00,sf) != 0x4E574156) /* "NWAV" */
if (!check_extensions(sf,"nwav,") )
goto fail;
#ifdef VGM_USE_VORBIS
{
ogg_vorbis_meta_info_t ovmi = {0};
int channels;
/* 0x04: version? */
/* 0x08: crc? */
ovmi.stream_size = read_32bitLE(0x0c, sf);
ovmi.loop_end = read_32bitLE(0x10, sf); /* num_samples, actually */
ovmi.stream_size = read_u32le(0x0c, sf);
ovmi.loop_end = read_u32le(0x10, sf); /* num_samples, actually */
/* 0x14: sample rate */
/* 0x18: bps? (16) */
channels = read_8bit(0x19, sf);
start_offset = read_16bitLE(0x1a, sf);
channels = read_u8(0x19, sf);
start_offset = read_u16le(0x1a, sf);
ovmi.loop_flag = read_16bitLE(0x1c, sf) != 0; /* loop count? -1 = loops */
ovmi.loop_flag = read_u16le(0x1c, sf) != 0; /* loop count? -1 = loops */
/* 0x1e: always 2? */
/* 0x20: always 1? */
ovmi.loop_start = read_32bitLE(0x24, sf);
ovmi.loop_start = read_u32le(0x24, sf);
/* 0x28: always 1? */
/* 0x2a: always 1? */
/* 0x2c: always null? */
@ -43,15 +41,9 @@ VGMSTREAM * init_vgmstream_nwav(STREAMFILE *sf) {
ovmi.loop_start = ovmi.loop_start / sizeof(int16_t) / channels;
ovmi.loop_end = ovmi.loop_end / sizeof(int16_t) / channels;
vgmstream = init_vgmstream_ogg_vorbis_config(sf, start_offset, &ovmi);
return init_vgmstream_ogg_vorbis_config(sf, start_offset, &ovmi);
}
#else
goto fail;
#endif
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -1,4 +1,3 @@
#ifdef VGM_USE_VORBIS
#include <stdio.h>
#include <string.h>
#include "meta.h"
@ -6,6 +5,28 @@
#include "ogg_vorbis_streamfile.h"
#ifdef VGM_USE_VORBIS
static VGMSTREAM* _init_vgmstream_ogg_vorbis(STREAMFILE* sf);
static VGMSTREAM* _init_vgmstream_ogg_vorbis_config(STREAMFILE* sf, off_t start, const ogg_vorbis_meta_info_t* ovmi);
#endif
VGMSTREAM* init_vgmstream_ogg_vorbis(STREAMFILE* sf) {
#ifdef VGM_USE_VORBIS
return _init_vgmstream_ogg_vorbis(sf);
#else
return NULL;
#endif
}
VGMSTREAM* init_vgmstream_ogg_vorbis_config(STREAMFILE* sf, off_t start, const ogg_vorbis_meta_info_t* ovmi) {
#ifdef VGM_USE_VORBIS
return _init_vgmstream_ogg_vorbis_config(sf, start, ovmi);
#else
return NULL;
#endif
}
#ifdef VGM_USE_VORBIS
static void um3_ogg_decryption_callback(void* ptr, size_t size, size_t nmemb, void* datasource) {
uint8_t *ptr8 = ptr;
size_t bytes_read = size * nmemb;
@ -110,8 +131,8 @@ static const uint32_t xiph_mappings[] = {
};
/* Ogg Vorbis, may contain loop comments */
VGMSTREAM* init_vgmstream_ogg_vorbis(STREAMFILE* sf) {
/* Ogg Vorbis - standard .ogg with (possibly) loop comments/metadata */
static VGMSTREAM* _init_vgmstream_ogg_vorbis(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
STREAMFILE* temp_sf = NULL;
ogg_vorbis_io_config_data cfg = {0};
@ -416,7 +437,7 @@ VGMSTREAM* init_vgmstream_ogg_vorbis(STREAMFILE* sf) {
ovmi.meta_type = meta_OGG_VORBIS;
}
vgmstream = init_vgmstream_ogg_vorbis_config(temp_sf != NULL ? temp_sf : sf, start_offset, &ovmi);
vgmstream = _init_vgmstream_ogg_vorbis_config(temp_sf != NULL ? temp_sf : sf, start_offset, &ovmi);
close_streamfile(temp_sf);
return vgmstream;
@ -426,7 +447,7 @@ fail:
return NULL;
}
VGMSTREAM* init_vgmstream_ogg_vorbis_config(STREAMFILE* sf, off_t start, const ogg_vorbis_meta_info_t* ovmi) {
static VGMSTREAM* _init_vgmstream_ogg_vorbis_config(STREAMFILE* sf, off_t start, const ogg_vorbis_meta_info_t* ovmi) {
VGMSTREAM* vgmstream = NULL;
ogg_vorbis_codec_data* data = NULL;
ogg_vorbis_io io = {0};

View File

@ -3,15 +3,14 @@
/* OGV - .ogg container (not related to ogv video) [Bloody Rondo (PC)] */
VGMSTREAM* init_vgmstream_ogv_3rdeye(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
off_t subfile_offset, subfile_size;
uint32_t subfile_offset, subfile_size;
/* checks */
if (!check_extensions(sf,"ogv"))
goto fail;
if (!is_id32be(0x00,sf, "OGV\0"))
goto fail;
if (!check_extensions(sf,"ogv"))
goto fail;
/* 0x04: PCM size */
subfile_size = read_u32le(0x08, sf);
@ -20,21 +19,15 @@ VGMSTREAM* init_vgmstream_ogv_3rdeye(STREAMFILE* sf) {
/* no loops (files bgm does full loops but sfx doesn't) */
#ifdef VGM_USE_VORBIS
{
ogg_vorbis_meta_info_t ovmi = {0};
ovmi.meta_type = meta_OGV_3RDEYE;
ovmi.stream_size = subfile_size;
vgmstream = init_vgmstream_ogg_vorbis_config(sf, subfile_offset, &ovmi);
return init_vgmstream_ogg_vorbis_config(sf, subfile_offset, &ovmi);
}
#else
goto fail;
#endif
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -132,15 +132,27 @@ VGMSTREAM* init_vgmstream_psb(STREAMFILE* sf) {
switch(psb.codec) {
case PCM:
if (psb.layers > 1) {
/* somehow R offset can go before L, use layered */
vgmstream->layout_data = build_layered_psb(sf, &psb);
if (!vgmstream->layout_data) goto fail;
vgmstream->layout_type = layout_layered;
if (!vgmstream->num_samples)
vgmstream->num_samples = pcm_bytes_to_samples(psb.stream_size[0], 1, psb.bps);
}
else {
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = psb.block_size / psb.channels;
if (!vgmstream->num_samples)
vgmstream->num_samples = pcm_bytes_to_samples(psb.stream_size[0], psb.channels, psb.bps);
}
switch(psb.bps) {
case 16: vgmstream->coding_type = coding_PCM16LE; break; /* Legend of Mana (PC), Namco Museum Archives Vol.1 (PC) */
case 24: vgmstream->coding_type = coding_PCM24LE; break; /* Legend of Mana (PC) */
default: goto fail;
}
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = psb.block_size / psb.channels;
if (!vgmstream->num_samples)
vgmstream->num_samples = pcm_bytes_to_samples(psb.stream_size[0], psb.channels, psb.bps);
break;
case MSADPCM: /* [Senxin Aleste (AC)] */
@ -235,7 +247,7 @@ static segmented_layout_data* build_segmented_psb_opus(STREAMFILE* sf, psb_heade
uint32_t samples[] = {psb->intro_samples, psb->body_samples};
uint32_t skips[] = {0, psb->skip_samples};
/* intro + body (looped songs) or just body (standard songs)
/* intro + body (looped songs) or just body (standard songs)
in full loops intro is 0 samples with a micro 1-frame opus [Nekopara (Switch)] */
if (offsets[0] && samples[0])
segment_count++;
@ -279,6 +291,21 @@ fail:
return NULL;
}
static VGMSTREAM* try_init_vgmstream(STREAMFILE* sf, init_vgmstream_t init_vgmstream, const char* extension, uint32_t offset, uint32_t size) {
STREAMFILE* temp_sf = NULL;
VGMSTREAM* v = NULL;
temp_sf = setup_subfile_streamfile(sf, offset, size, extension);
if (!temp_sf) goto fail;
v = init_vgmstream(temp_sf);
close_streamfile(temp_sf);
return v;
fail:
return NULL;
}
static layered_layout_data* build_layered_psb(STREAMFILE* sf, psb_header_t* psb) {
layered_layout_data* data = NULL;
int i;
@ -289,25 +316,39 @@ static layered_layout_data* build_layered_psb(STREAMFILE* sf, psb_header_t* psb)
if (!data) goto fail;
for (i = 0; i < psb->layers; i++) {
STREAMFILE* temp_sf = NULL;
VGMSTREAM* (*init_vgmstream)(STREAMFILE* sf) = NULL;
const char* extension = NULL;
switch (psb->codec) {
case DSP:
extension = "adpcm";
init_vgmstream = init_vgmstream_ngc_dsp_std_le;
case PCM: {
VGMSTREAM* v = allocate_vgmstream(1, 0);
if (!v) goto fail;
data->layers[i] = v;
v->sample_rate = psb->sample_rate;
v->num_samples = psb->num_samples;
switch(psb->bps) {
case 16: v->coding_type = coding_PCM16LE; break;
case 24: v->coding_type = coding_PCM24LE; break;
default: goto fail;
}
v->layout_type = layout_none;
if (!v->num_samples)
v->num_samples = pcm_bytes_to_samples(psb->stream_size[i], 1, psb->bps);
if (!vgmstream_open_stream(v, sf, psb->stream_offset[i]))
goto fail;
break;
}
case DSP:
data->layers[i] = try_init_vgmstream(sf, init_vgmstream_ngc_dsp_std_le, "adpcm", psb->stream_offset[i], psb->stream_size[i]);
if (!data->layers[i]) goto fail;
break;
default:
VGM_LOG("psb: layer not implemented\n");
goto fail;
}
temp_sf = setup_subfile_streamfile(sf, psb->stream_offset[i], psb->stream_size[i], extension);
if (!temp_sf) goto fail;
data->layers[i] = init_vgmstream(temp_sf);
close_streamfile(temp_sf);
if (!data->layers[i]) goto fail;
}
/* setup layered VGMSTREAMs */
@ -332,9 +373,9 @@ static int prepare_fmt(STREAMFILE* sf, psb_header_t* psb) {
psb->format = read_u16be(offset + 0x00,sf);
psb->channels = read_u16be(offset + 0x02,sf);
psb->sample_rate = read_u32be(offset + 0x04,sf);
xma2_parse_fmt_chunk_extra(sf,
offset,
&psb->loop_flag,
xma2_parse_fmt_chunk_extra(sf,
offset,
&psb->loop_flag,
&psb->num_samples,
&psb->loop_start,
&psb->loop_end,
@ -347,7 +388,7 @@ static int prepare_fmt(STREAMFILE* sf, psb_header_t* psb) {
psb->block_size = read_u16le(offset + 0x0c,sf);
psb->bps = read_u16le(offset + 0x0e,sf);
/* 0x10+ varies */
switch(psb->format) {
case 0x0002:
if (!msadpcm_check_coefs(sf, offset + 0x14))
@ -356,7 +397,7 @@ static int prepare_fmt(STREAMFILE* sf, psb_header_t* psb) {
default:
break;
}
}
return 1;
@ -392,11 +433,12 @@ static int prepare_codec(STREAMFILE* sf, psb_header_t* psb) {
/* try console strings */
if (!spec)
goto fail;
if (strcmp(spec, "nx") == 0) {
if (!ext)
goto fail;
/* common, multichannel */
if (strcmp(ext, ".opus") == 0) {
psb->codec = OPUSNX;
@ -409,12 +451,22 @@ static int prepare_codec(STREAMFILE* sf, psb_header_t* psb) {
return 1;
}
/* Legend of Mana (Switch), layered */
if (strcmp(ext, ".adpcm") == 0) {
psb->codec = DSP;
psb->channels = psb->layers;
return 1;
}
/* Castlevania Advance Collection (Switch), layered */
if (strcmp(ext, ".p16") == 0) {
psb->codec = PCM;
psb->bps = 16;
psb->channels = psb->layers;
return 1;
}
}
if (strcmp(spec, "ps3") == 0) {
@ -477,7 +529,7 @@ static int prepare_psb_extra(STREAMFILE* sf, psb_header_t* psb) {
goto fail;
return 1;
fail:
return 0;
return 0;
}
@ -488,7 +540,7 @@ fail:
* - body/channelCount/ext/intro/loop/samprate [Legend of Mana (Switch)]
* - body: data/sampleCount/skipSampleCount, intro: data/sampleCount
* - data/dpds/fmt/wav/loop
* - pan: array [N.0 .. 0.N] (when N layers, in practice just a wonky L/R definition)
* - pan: array [N.0 .. 0.N] (when N layers, in practice just a wonky L/R definition)
*/
static int parse_psb_channels(psb_header_t* psb, psb_node_t* nchans) {
int i;
@ -584,7 +636,7 @@ static int parse_psb_channels(psb_header_t* psb, psb_node_t* nchans) {
};
}
/* background: false?
/* background: false?
*/
break;
@ -659,7 +711,7 @@ fail:
* - "archData": (main audio part, varies per game/platform/codec)
* - "device": ?
* ...
* - (voice name N): ...
* - (voice name N): ...
* From decompilations, audio code reads common keys up to "archData", then depends on game (not unified).
* Keys are (seemingly) stored in text order.
*/

View File

@ -12,8 +12,8 @@ VGMSTREAM* init_vgmstream_sps_n1(STREAMFILE* sf) {
off_t subfile_offset;
size_t subfile_size;
VGMSTREAM* (*init_vgmstream_subfile)(STREAMFILE*) = NULL;
const char* extension;
init_vgmstream_t init_vgmstream = NULL;
const char* extension = NULL;
uint32_t (*read_u32)(off_t,STREAMFILE*);
uint16_t (*read_u16)(off_t,STREAMFILE*);
@ -38,12 +38,12 @@ VGMSTREAM* init_vgmstream_sps_n1(STREAMFILE* sf) {
switch(type) {
case 1:
init_vgmstream_subfile = init_vgmstream_vag;
init_vgmstream = init_vgmstream_vag;
extension = "vag";
break;
case 2:
init_vgmstream_subfile = init_vgmstream_riff;
init_vgmstream = init_vgmstream_riff;
extension = "at3";
break;
@ -59,7 +59,7 @@ VGMSTREAM* init_vgmstream_sps_n1(STREAMFILE* sf) {
temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, extension);
if (!temp_sf) goto fail;
vgmstream = init_vgmstream_subfile(temp_sf);
vgmstream = init_vgmstream(temp_sf);
if (!vgmstream) goto fail;
vgmstream->sample_rate = sample_rate; /* .vag header doesn't match */
@ -81,7 +81,7 @@ VGMSTREAM* init_vgmstream_sps_n1_segmented(STREAMFILE* sf) {
int loop_flag, type, sample_rate;
int i, segment;
VGMSTREAM* (*init_vgmstream_subfile)(STREAMFILE*) = NULL;
init_vgmstream_t init_vgmstream = NULL;
const char* extension;
segmented_layout_data* data = NULL;
int segment_count, loop_start_segment, loop_end_segment;
@ -101,15 +101,13 @@ VGMSTREAM* init_vgmstream_sps_n1_segmented(STREAMFILE* sf) {
/* 0x0c: num_samples (slightly smaller than added samples?) */
switch(type) {
#ifdef VGM_USE_VORBIS
case 7:
init_vgmstream_subfile = init_vgmstream_ogg_vorbis;
init_vgmstream = init_vgmstream_ogg_vorbis;
extension = "ogg";
break;
#endif
case 9:
init_vgmstream_subfile = init_vgmstream_opus_std;
init_vgmstream = init_vgmstream_opus_std;
extension = "opus";
break;
@ -155,7 +153,7 @@ VGMSTREAM* init_vgmstream_sps_n1_segmented(STREAMFILE* sf) {
temp_sf = setup_subfile_streamfile(sf, segment_offset,segment_size, extension);
if (!temp_sf) goto fail;
data->segments[segment] = init_vgmstream_subfile(temp_sf);
data->segments[segment] = init_vgmstream(temp_sf);
close_streamfile(temp_sf);
if (!data->segments[segment]) goto fail;

View File

@ -213,11 +213,6 @@ static int parse_header(STREAMFILE* sf_h, STREAMFILE* sf_b, strwav_header* strwa
* table entries don't need to match (table2 may be slightly bigger)
*/
//breaking ta rules full test again, fuse with Pac-Man World 3
//same on xbox and pc
//same with zapper + pw3 gc
//todo loop start/end values may be off for some headers
/* Fuzion Frenzy (Xbox)[2001] wma */
@ -251,7 +246,7 @@ static int parse_header(STREAMFILE* sf_h, STREAMFILE* sf_b, strwav_header* strwa
/* Cubix Robots for Everyone: Showdown (GC)[2003] */
if ( read_u32be(0x04,sf_h) == 0x00000900 &&
read_u32be(0x0c,sf_h) != header_size &&
read_u32le(0x24,sf_h) != 0 &&
read_u32be(0x24,sf_h) != 0 &&
read_u32be(0x24,sf_h) == read_u32be(0x90,sf_h) && /* sample rate repeat */
read_u32be(0xa0,sf_h) == header_size /* ~0x3C0 */
) {
@ -367,9 +362,11 @@ static int parse_header(STREAMFILE* sf_h, STREAMFILE* sf_b, strwav_header* strwa
return 1;
}
/* Zapper: One Wicked Cricket! (GC)[2005] */
/* Zapper: One Wicked Cricket! Beta (GC)[2002] */
/* Zapper: One Wicked Cricket! (GC)[2002] */
if ( read_u32be(0x04,sf_h) == 0x00000900 &&
read_u32be(0x24,sf_h) == read_u32be(0xB0,sf_h) && /* sample rate repeat */
read_u32be(0x88,sf_h) != 0 &&
read_u32le(0xc0,sf_h) == header_size /* LE! */
) {
/* 0x08: null */
@ -399,10 +396,36 @@ static int parse_header(STREAMFILE* sf_h, STREAMFILE* sf_b, strwav_header* strwa
return 1;
}
/* Zapper: One Wicked Cricket! Beta (PS2)[2002] */
if ( read_u32be(0x04,sf_h) == 0x00000900 &&
read_u32le(0x2c,sf_h) == 44100 && /* sample rate */
read_u32le(0x70,sf_h) == 0 && /* sample rate repeat? */
header_size == 0x78
) {
/* 0x08: null */
/* 0x0c: hashname */
/* 0x28: loop start? */
strwav->sample_rate = read_s32le(0x2c,sf_h);
/* 0x30: number of 0x800 sectors */
strwav->flags = read_u32le(0x34,sf_h);
strwav->num_samples = read_s32le(0x5c,sf_h);
strwav->tracks = read_s32le(0x60,sf_h);
strwav->loop_start = 0;
strwav->loop_end = 0;
strwav->codec = PSX;
strwav->interleave = strwav->tracks > 1 ? 0x8000 : 0x8000;
//todo: tracks are stereo blocks of size 0x20000*tracks, containing 4 interleaves of 0x8000:
// | 1 2 1 2 | 3 4 3 4 | 5 6 5 6 | 1 2 1 2 | 3 4 3 4 | 5 6 5 6 | ...
;VGM_LOG("STR+WAV: header ZPb (PS2)\n");
return 1;
}
/* Zapper: One Wicked Cricket! (PS2)[2002] */
/* The Fairly OddParents - Breakin' da Rules (PS2)[2003] */
/* The Fairly OddParents! - Shadow Showdown (PS2)[2004] */
/* Bad Boys II (PS2)[2004] */
/* Zapper: One Wicked Cricket! (PS2)[2005] */
if ((read_u32be(0x04,sf_h) == 0x00000800 || /* BB2 */
read_u32be(0x04,sf_h) == 0x00000900) && /* FOP, ZP */
read_u32le(0x24,sf_h) == read_u32le(0x70,sf_h) && /* sample rate repeat */
@ -456,7 +479,62 @@ static int parse_header(STREAMFILE* sf_h, STREAMFILE* sf_b, strwav_header* strwa
return 1;
}
/* Zapper: One Wicked Cricket! (PC)[2005] */
/* Zapper: One Wicked Cricket! Beta (Xbox)[2002] */
if ( read_u32be(0x04,sf_h) == 0x00000900 &&
read_u32le(0x0c,sf_h) != header_size &&
read_u32le(0x24,sf_h) != 0 &&
read_u32le(0x24,sf_h) == read_u32le(0x90,sf_h) && /* sample rate repeat */
read_u32le(0xa0,sf_h) == header_size /* ~0xC0 */
) {
/* 0x08: null */
/* 0x0c: hashname */
strwav->num_samples = read_s32le(0x20,sf_h);
strwav->sample_rate = read_s32le(0x24,sf_h);
/* 0x28: 16 bps */
strwav->flags = read_u32le(0x2c,sf_h);
strwav->loop_start = read_s32le(0x38,sf_h);
strwav->tracks = read_s32le(0x50,sf_h);
/* 0x58: number of chunks? */
/* 0x90: sample rate 2 */
/* 0xb8: total frames? */
strwav->loop_end = strwav->num_samples;
strwav->codec = XBOX;
strwav->interleave = strwav->tracks > 1 ? 0xD800/2 : 0xD800;
;VGM_LOG("STR+WAV: header ZPb (Xbox)\n");
return 1;
}
/* Zapper: One Wicked Cricket! (Xbox)[2002] */
if ( read_u32be(0x04,sf_h) == 0x00000900 &&
read_u32le(0x0c,sf_h) != header_size &&
read_u32le(0x24,sf_h) != 0 &&
read_u32le(0x24,sf_h) == read_u32le(0xb0,sf_h) && /* sample rate repeat */
read_u32le(0xc0,sf_h) == header_size
) {
/* 0x08: null */
/* 0x0c: hashname */
strwav->num_samples = read_s32le(0x20,sf_h);
strwav->sample_rate = read_s32le(0x24,sf_h);
/* 0x28: 16 bps */
strwav->flags = read_u32le(0x2c,sf_h);
strwav->loop_start = read_s32le(0x38,sf_h);
strwav->tracks = read_s32le(0x70,sf_h);
/* 0x78: number of chunks? */
/* 0xb0: sample rate 2 */
/* 0xc0: header size*/
/* 0xd8: total frames? */
strwav->loop_end = strwav->num_samples;
strwav->codec = XBOX;
strwav->interleave = strwav->tracks > 1 ? 0xD800/2 : 0xD800;
;VGM_LOG("STR+WAV: header ZP (Xbox)\n");
return 1;
}
/* Zapper: One Wicked Cricket! (PC)[2002] */
if ( read_u32be(0x04,sf_h) == 0x00000900 &&
read_u32le(0x24,sf_h) == read_u32le(0x114,sf_h) && /* sample rate repeat */
read_u32le(0x12c,sf_h) == header_size /* ~0x130 */
@ -514,32 +592,6 @@ static int parse_header(STREAMFILE* sf_h, STREAMFILE* sf_b, strwav_header* strwa
return 1;
}
/* Zapper: One Wicked Cricket! Beta (PS2)[2005] */
if ( read_u32be(0x04,sf_h) == 0x00000900 &&
read_u32le(0x2c,sf_h) == 44100 && /* sample rate */
read_u32le(0x70,sf_h) == 0 && /* sample rate repeat? */
header_size == 0x78
) {
/* 0x08: null */
/* 0x0c: hashname */
/* 0x28: loop start? */
strwav->sample_rate = read_s32le(0x2c,sf_h);
/* 0x30: number of 0x800 sectors */
strwav->flags = read_u32le(0x34,sf_h);
strwav->num_samples = read_s32le(0x5c,sf_h);
strwav->tracks = read_s32le(0x60,sf_h);
strwav->loop_start = 0;
strwav->loop_end = 0;
strwav->codec = PSX;
strwav->interleave = strwav->tracks > 1 ? 0x8000 : 0x8000;
//todo: tracks are stereo blocks of size 0x20000*tracks, containing 4 interleaves of 0x8000:
// | 1 2 1 2 | 3 4 3 4 | 5 6 5 6 | 1 2 1 2 | 3 4 3 4 | 5 6 5 6 | ...
;VGM_LOG("STR+WAV: header ZPb (PS2)\n");
return 1;
}
/* Pac-Man World 3 (GC)[2005] */
/* SpongeBob SquarePants: Creature from the Krusty Krab (GC)[2006] */
/* SpongeBob SquarePants: Creature from the Krusty Krab (Wii)[2006] */

View File

@ -56,15 +56,19 @@ typedef struct {
static int parse_wwise(STREAMFILE* sf, wwise_header* ww);
static int is_dsp_full_interleave(STREAMFILE* sf, wwise_header* ww, off_t coef_offset);
typedef uint32_t (*read_u32_t)(off_t, STREAMFILE*);
typedef int32_t (*read_s32_t)(off_t, STREAMFILE*);
typedef uint16_t (*read_u16_t)(off_t, STREAMFILE*);
/* Wwise - Audiokinetic Wwise (WaveWorks Interactive Sound Engine) middleware */
VGMSTREAM* init_vgmstream_wwise(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
wwise_header ww = {0};
off_t start_offset;
uint32_t (*read_u32)(off_t,STREAMFILE*) = NULL;
int32_t (*read_s32)(off_t,STREAMFILE*) = NULL;
uint16_t (*read_u16)(off_t,STREAMFILE*) = NULL;
read_u32_t read_u32 = NULL;
read_s32_t read_s32 = NULL;
read_u16_t read_u16 = NULL;
/* checks */
@ -99,6 +103,7 @@ VGMSTREAM* init_vgmstream_wwise(STREAMFILE* sf) {
vgmstream->loop_start_sample = ww.loop_start_sample;
vgmstream->loop_end_sample = ww.loop_end_sample;
vgmstream->channel_layout = ww.channel_layout;
vgmstream->stream_size = ww.data_size;
switch(ww.codec) {
case PCM: /* common */
@ -696,11 +701,12 @@ static int is_dsp_full_interleave(STREAMFILE* sf, wwise_header* ww, off_t coef_o
static int parse_wwise(STREAMFILE* sf, wwise_header* ww) {
uint32_t (*read_u32)(off_t,STREAMFILE*) = NULL;
uint16_t (*read_u16)(off_t,STREAMFILE*) = NULL;
read_u32_t read_u32;
read_u16_t read_u16;
ww->big_endian = is_id32be(0x00,sf, "RIFX");
if (ww->big_endian) { /* Wwise honors machine's endianness (PC=RIFF, X360=RIFX --unlike XMA) */
/* Wwise honors machine's endianness (PC=RIFF, X360=RIFX --unlike XMA) */
ww->big_endian = is_id32be(0x00,sf, "RIFX"); /* RIFF size not useful to detect, see below */
if (ww->big_endian) {
read_u32 = read_u32be;
read_u16 = read_u16be;
} else {

View File

@ -17,10 +17,10 @@ VGMSTREAM* init_vgmstream_xnb(STREAMFILE* sf) {
/* checks */
if (!check_extensions(sf,"xnb"))
goto fail;
if ((read_u32be(0x00, sf) & 0xFFFFFF00) != get_id32be("XNB\0"))
goto fail;
if (!check_extensions(sf,"xnb"))
goto fail;
/* XNA Studio platforms: 'w' = Windows, 'm' = Windows Phone 7, 'x' = X360
* MonoGame extensions: 'i' = iOS, 'a' = Android, 'X' = MacOSX, 'P' = PS4, 'S' = Switch, etc */
@ -184,10 +184,9 @@ VGMSTREAM* init_vgmstream_xnb(STREAMFILE* sf) {
if (!temp_sf) goto fail;
if (is_ogg) {
#ifdef VGM_USE_VORBIS
vgmstream = init_vgmstream_ogg_vorbis(temp_sf);
#endif
} else {
}
else {
vgmstream = init_vgmstream_riff(temp_sf);
}
close_streamfile(temp_sf);

View File

@ -16,11 +16,11 @@
* - Clang: seems only defined on Linux/GNU environments, somehow emscripten is out
* (unsure about Clang Win since apparently they define _MSC_VER)
* - Android: API +24 if not using __USE_FILE_OFFSET64
* Not sure if fopen64 is needed in some cases. May be worth adding some compiler flag to enable 64 versions manually.
* Not sure if fopen64 is needed in some cases.
*/
/* MSVC fixes (though mingw uses MSVCRT but not MSC_VER, maybe use AND?) */
#if defined(__MSVCRT__) || defined(_MSC_VER)
#if defined(_MSC_VER) //&& defined(__MSVCRT__)
/* MSVC fixes (MinG64 seems to set MSVCRT too, but we want it below) */
#include <io.h>
#define fopen_v fopen
@ -43,15 +43,23 @@
// #define off_t/off64_t __int64
//#endif
#elif defined(VGMSTREAM_USE_IO64) || defined(__MINGW32__) || defined(__MINGW64__)
/* force, or known to work */
#define fopen_v fopen
#define fseek_v fseeko64 //fseeko
#define ftell_v ftello64 //ftello
#elif defined(XBMC) || defined(__EMSCRIPTEN__) || defined (__ANDROID__)
/* may depend on version */
#define fopen_v fopen
#define fseek_v fseek
#define ftell_v ftell
#else
/* other Linux systems may already use off64_t in fseeko/ftello? */
#define fopen_v fopen
#define fseek_v fseeko64 //fseeko
#define ftell_v ftello64 //ftello
#define fseek_v fseeko
#define ftell_v ftello
#endif

View File

@ -59,9 +59,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
init_vgmstream_caf,
init_vgmstream_vpk,
init_vgmstream_genh,
#ifdef VGM_USE_VORBIS
init_vgmstream_ogg_vorbis,
#endif
init_vgmstream_sli_ogg,
init_vgmstream_sfl_ogg,
init_vgmstream_sadb,