mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-01-19 00:04:04 +01:00
Merge pull request #1559 from bnnm/api-misc2
- Fix some Ongakukan .adp + cleanup - cleanup
This commit is contained in:
commit
644da84023
60
README.md
60
README.md
@ -21,7 +21,7 @@ The main development repository: https://github.com/vgmstream/vgmstream/
|
||||
Automated builds with the latest changes: https://vgmstream.org
|
||||
(https://github.com/vgmstream/vgmstream-releases/releases/tag/nightly)
|
||||
|
||||
Common releases: https://github.com/vgmstream/vgmstream/releases
|
||||
Numbered releases: https://github.com/vgmstream/vgmstream/releases
|
||||
|
||||
Help can be found here: https://www.hcs64.com/
|
||||
|
||||
@ -39,56 +39,54 @@ There are multiple end-user components:
|
||||
The main library (plain *vgmstream*) is the code that handles the internal conversion, while the
|
||||
above components are what you use to get sound.
|
||||
|
||||
If you want to convert game audio to `.wav`, try getting *vgmstream-cli* (see below) then
|
||||
drag-and-drop one or more files to the executable (support may vary per O.S. or distro).
|
||||
This should create `(file.extension).wav`, if the format is supported. More user-friendly
|
||||
would be installing a player like *foobar2000* (for Windows) or *Audacious* (for Linux)
|
||||
and the vgmstream plugin. Then you can directly listen your files and set options like infinite
|
||||
looping, or convert to `.wav` with the player's options (also easier if your file has multiple
|
||||
"subsongs").
|
||||
### Usage
|
||||
If you want to convert game audio to `.wav`, get *vgmstream-cli* then drag-and-drop one
|
||||
or more files to the executable (support may vary per O.S. or distro). This should create
|
||||
`(file.extension).wav`, if the format is supported. You can also try the online web player
|
||||
instead. See: https://vgmstream.org
|
||||
|
||||
More user-friendly would be installing a player like *foobar2000* (on Windows) or *Audacious*
|
||||
(on Linux) and the vgmstream plugin. Then you can directly listen your files and set options like
|
||||
infinite looping, or convert to `.wav` with the player's options (also easier to use if your file
|
||||
has multiple "subsongs").
|
||||
|
||||
See [components](doc/USAGE.md#components) in the *usage guide* for full install instructions and
|
||||
explanations. The aim is feature parity, but there are a few differences between them due to
|
||||
missing parts on vgmstream's side or lack of support in the player.
|
||||
|
||||
Note that vgmstream cannot *encode* (convert from `.wav` to a video game format), it only *decodes*
|
||||
Note that vgmstream cannot *encode* (convert from `.wav` to a game format), it only *decodes*
|
||||
(plays game audio).
|
||||
|
||||
|
||||
### Windows
|
||||
Get the latest prebuilt binaries (CLI/plugins/etc) on our website:
|
||||
- https://vgmstream.org
|
||||
|
||||
Or the less frequent "official" releases on GitHub:
|
||||
- https://github.com/vgmstream/vgmstream/releases
|
||||
### Windows binaries
|
||||
Prebuilt binaries:
|
||||
- https://vgmstream.org (latest)
|
||||
- https://github.com/vgmstream/vgmstream/releases (infrequent numbered releases)
|
||||
|
||||
The foobar2000 component is also available on https://www.foobar2000.org based on current
|
||||
release.
|
||||
|
||||
If the above links fail, you may also try the alternative versions built by
|
||||
[bnnm](https://github.com/bnnm):
|
||||
You may also try the alternative versions (irregularly) built by [bnnm](https://github.com/bnnm):
|
||||
- https://github.com/bnnm/vgmstream-builds/raw/master/bin/vgmstream-latest-test-u.zip
|
||||
|
||||
You may compile from source as well, see the [build guide](doc/BUILD.md).
|
||||
Or compile from source, see the [build guide](doc/BUILD.md).
|
||||
|
||||
### Linux
|
||||
### Linux binaries
|
||||
A prebuilt CLI binary is available. It's statically linked and should work on systems running
|
||||
Linux kernel v3.2 and above:
|
||||
- https://vgmstream.org
|
||||
- https://github.com/vgmstream/vgmstream/releases
|
||||
- https://vgmstream.org (latest)
|
||||
- https://github.com/vgmstream/vgmstream/releases (infrequent numbered releases)
|
||||
|
||||
Building from source will also give you *vgmstream.so* (Audacious plugin), and *vgmstream123*
|
||||
(command-line player).
|
||||
(command-line player), which can't be statically linked.
|
||||
|
||||
When building, many extra components have to be installed or compiled separately, which the
|
||||
[build guide](doc/BUILD.md) describes in detail. For a quick build on Debian and Ubuntu-style
|
||||
distributions run `./make-build-cmake.sh`. The script will need to install various dependencies,
|
||||
so you may prefer to copy commands and run them manually.
|
||||
When building it needs several external libraries. For a quick script for Debian and Ubuntu-style
|
||||
distros run `./make-build-cmake.sh`. The script will need to install dependencies first, so you
|
||||
may prefer to run steps manually, which the [build guide](doc/BUILD.md) describes in detail.
|
||||
|
||||
### macOS
|
||||
A prebuilt CLI binary is available as well:
|
||||
- https://vgmstream.org
|
||||
- https://github.com/vgmstream/vgmstream/releases
|
||||
### macOS binaries
|
||||
A prebuilt CLI binary is available:
|
||||
- https://vgmstream.org (latest)
|
||||
- https://github.com/vgmstream/vgmstream/releases (infrequent numbered releases)
|
||||
|
||||
Otherwise follow the [build guide](doc/BUILD.md).
|
||||
|
||||
|
@ -268,8 +268,7 @@ static void test_libsf_size(libvgmstream_streamfile_t* libsf) {
|
||||
static void test_libsf_name(libvgmstream_streamfile_t* libsf) {
|
||||
VGM_STEP();
|
||||
|
||||
char name[128];
|
||||
libsf->get_name(libsf->user_data, name, sizeof(name));
|
||||
const char* name = libsf->get_name(libsf->user_data);
|
||||
assert(strcmp(name, "api.bin") == 0);
|
||||
}
|
||||
|
||||
|
@ -103,7 +103,7 @@ different internally (encrypted, different versions, etc) and not always can be
|
||||
- Kuju London .KWA header [*DSP_KWA*]
|
||||
- Koei Tecmo APEX header [*DSP_APEX*]
|
||||
- Rebellion DSP header [*DSP_ASURA*]
|
||||
- *ngc_dsp_std*: `.dsp .adp .(extensionless) .wav .lwav .dat .ldat`
|
||||
- *ngc_dsp_std*: `.dsp .adp .(extensionless) .wav .lwav .dat .ldat .rsm`
|
||||
- *ngc_dsp_std_le*: `.adpcm`
|
||||
- *ngc_mdsp_std*: `.dsp .mdsp`
|
||||
- *ngc_dsp_stm*: `.stm .lstm .dsp`
|
||||
@ -193,6 +193,7 @@ different internally (encrypted, different versions, etc) and not always can be
|
||||
- *vag*: `.vag .swag .str .vig .l .r .vas .xa2 .snd .svg .(extensionless) .wav .lwav`
|
||||
- *vag_aaap*: `.vag`
|
||||
- *vag_footer*: `.(extensionless) .vag`
|
||||
- *vag_evolution_games*: `.vag`
|
||||
- Codecs: PSX HEVAG
|
||||
- **ild.c**
|
||||
- Tose ILD header [*ILD*]
|
||||
@ -202,27 +203,17 @@ different internally (encrypted, different versions, etc) and not always can be
|
||||
- Cauldron .STR header [*DSP_STR*]
|
||||
- *ngc_str*: `.str`
|
||||
- Codecs: NGC_DSP
|
||||
- **ea_schl.c**
|
||||
- Electronic Arts BNK header [*EA_BNK*]
|
||||
- Electronic Arts SCHl header [*EA_SCHL*]
|
||||
- **ea_schl_standard.c**
|
||||
- (container)
|
||||
- *ea_schl*: `.asf .lasf .str .chk .eam .exa .sng .aud .sx .xa .strm .stm .hab .xsf .gsf .(extensionless) .r`
|
||||
- *ea_schl_video*: `.uv .dct .mad .wve .vp6 .mpc .lmpc`
|
||||
- *ea_bnk*: `.bnk .sdt .hdt .ldt .abk .ast .cat .(extensionless)`
|
||||
- *ea_abk*: `.abk + .ast`
|
||||
- *ea_hdr_dat*: `.hdr + .dat`
|
||||
- Subfiles: *vag*
|
||||
- *ea_hdr_dat_v2*: `.hdr + .dat .mus .(external)`
|
||||
- *ea_map_mus*: `.map .lin .mpf`
|
||||
- *ea_mpf_mus*: `.mpf`
|
||||
- *ea_msb_mus*: `.msb .msx`
|
||||
- *ea_mpf_mus_main*: `(base) + .(external)`
|
||||
- Codecs: EA_XA EA_XA_int EA_XA_V2 PCM8_int PCM16_int PCM8 PCM16LE PCM16BE PSX XBOX_IMA_int NGC_DSP VADPCM MPEG EA_MT ATRAC3
|
||||
- *ea_schl_video*: `.uv .dct .mpc .lmpc .vp6 .mad .wve`
|
||||
- **caf.c**
|
||||
- tri-Crescendo CAF Header [*CAF*]
|
||||
- tri-Crescendo CAF header [*CAF*]
|
||||
- *caf*: `.caf .cfn .(extensionless)`
|
||||
- Codecs: NGC_DSP
|
||||
- **vpk.c**
|
||||
- SCE America VPK Header [*VPK*]
|
||||
- SCE America VPK header [*VPK*]
|
||||
- *vpk*: `.vpk`
|
||||
- Codecs: PSX
|
||||
- **ogg_vorbis.c**
|
||||
@ -249,7 +240,7 @@ different internally (encrypted, different versions, etc) and not always can be
|
||||
- **aifc.c**
|
||||
- Apple AIFF-C header [*AIFC*]
|
||||
- Apple AIFF header [*AIFF*]
|
||||
- *aifc*: `.aif .laif .wav .lwav .aiff .laiff .(extensionless) .aifc .laifc .afc .cbd2 .bgm .fda .n64 .xa .caf .acm .adp .ai .pcm`
|
||||
- *aifc*: `.aif .laif .wav .lwav .aiff .laiff .(extensionless) .aifc .laifc .afc .cbd2 .bgm .fda .n64 .xa .caf .acm .adp .ai .pcm .vp6 .mpc .lmpc`
|
||||
- Codecs: SDX2 CBD2 DVI_IMA_int APPLE_IMA4 RELIC VADPCM PCM8 PCM16BE XA
|
||||
- **str_snds.c**
|
||||
- 3DO SNDS header [*STR_SNDS*]
|
||||
@ -263,9 +254,9 @@ different internally (encrypted, different versions, etc) and not always can be
|
||||
- CRI AHX header [*AHX*]
|
||||
- *ahx*: `.ahx`
|
||||
- Codecs: MPEG
|
||||
- **ivb.c**
|
||||
- IVB/BVII header [*PS2_IVB*]
|
||||
- *ivb*: `.ivb`
|
||||
- **iivb.c**
|
||||
- Vingt-et-un IIVB header [*IIVB*]
|
||||
- *iivb*: `.ivb`
|
||||
- Codecs: PSX
|
||||
- **svs.c**
|
||||
- Square SVS header [*SVS*]
|
||||
@ -281,7 +272,7 @@ different internally (encrypted, different versions, etc) and not always can be
|
||||
- RIFX WAVE header (smpl looping) [*RIFX_WAVE_smpl*]
|
||||
- *riff*: `.wav .lwav .xwav .mwv .da .dax .cd .med .snd .adx .adp .xss .xsew .adpcm .adw .wd .(extensionless) .sbv .wvx .str .at3 .rws .aud .at9 .ckd .saf .ima .nsa .pcm .xvag .ogg .logg .p1d .xms .mus .dat .ldat .wma .lwma .caf .wax .voi .se`
|
||||
- *rifx*: `.wav .lwav`
|
||||
- Codecs: AICA_int PCM32LE PCM24LE PCM16BE PCM16LE PCM8_U MSADPCM IMA PCMFLOAT MS_IMA AICA MPEG_custom XBOX_IMA MS_IMA_3BIT DVI_IMA L5_555 OGG_VORBIS ATRAC9 ATRAC3 MPEG MSADPCM_int
|
||||
- Codecs: AICA_int PCM32LE PCM24LE PCM16BE PCM16LE PCM8_U MSADPCM IMA PCMFLOAT MS_IMA AICA MPEG_custom XBOX_IMA MS_IMA_3BIT DVI_IMA L5_555 OGG_VORBIS ATRAC9 ATRAC3 MPEG MSADPCM_mono
|
||||
- **nwa.c**
|
||||
- VisualArt's NWA header (NWAINFO.INI looping) [*NWA_NWAINFOINI*]
|
||||
- VisualArt's NWA header (Gameexe.ini looping) [*NWA_GAMEEXEINI*]
|
||||
@ -307,7 +298,7 @@ different internally (encrypted, different versions, etc) and not always can be
|
||||
- *hgc1*: `.str`
|
||||
- Codecs: PSX
|
||||
- **aus.c**
|
||||
- Capcom AUS Header [*AUS*]
|
||||
- Atomic Planet AUS header [*AUS*]
|
||||
- *aus*: `.aus`
|
||||
- Codecs: XBOX_IMA PSX
|
||||
- **rws_80d.c**
|
||||
@ -320,9 +311,7 @@ different internally (encrypted, different versions, etc) and not always can be
|
||||
- FMOD FSB3 header [*FSB3*]
|
||||
- FMOD FSB4 header [*FSB4*]
|
||||
- *fsb*: `.fsb .bnk .sfx .ps3 .xen`
|
||||
- *fsb4_wav*: `.fsb .wii`
|
||||
- Subfiles: *fsb*
|
||||
- Codecs: MPEG XBOX_IMA FSB_IMA PSX XMA1 XMA2 NGC_DSP NGC_DSP_subint CELT_FSB PCM8_U PCM8 PCM16BE PCM16LE
|
||||
- Codecs: MPEG FSB_IMA XBOX_IMA PSX XMA1 XMA2 NGC_DSP NGC_DSP_subint CELT_FSB PCM8_U PCM8 PCM16BE PCM16LE
|
||||
- **fsb5.c**
|
||||
- FMOD FSB5 header [*FSB5*]
|
||||
- *fsb5*: `.fsb .snd`
|
||||
@ -349,7 +338,7 @@ different internally (encrypted, different versions, etc) and not always can be
|
||||
- *musx*: `.sfx .musx`
|
||||
- Codecs: PSX DAT4_IMA DVI_IMA_int XBOX_IMA NGC_DSP PCM16BE PCM16LE
|
||||
- **filp.c**
|
||||
- cavia FILp Header [*FILP*]
|
||||
- cavia FILp header [*FILP*]
|
||||
- *filp*: `.fil`
|
||||
- Codecs: PSX
|
||||
- **ikm.c**
|
||||
@ -377,11 +366,11 @@ different internally (encrypted, different versions, etc) and not always can be
|
||||
- *dc_kcey*: `.pcm .kcey`
|
||||
- Codecs: DVI_IMA
|
||||
- **rstm_rockstar.c**
|
||||
- Rockstar Games RSTM Header [*RSTM_ROCKSTAR*]
|
||||
- Rockstar Games RSTM header [*RSTM_ROCKSTAR*]
|
||||
- *rstm_rockstar*: `.rsm .rstm`
|
||||
- Codecs: PSX
|
||||
- **acm.c**
|
||||
- InterPlay ACM Header [*ACM*]
|
||||
- InterPlay ACM header [*ACM*]
|
||||
- *acm*: `.acm .tun .wavc`
|
||||
- Codecs: ACM
|
||||
- **mus_acm.c**
|
||||
@ -389,11 +378,11 @@ different internally (encrypted, different versions, etc) and not always can be
|
||||
- *mus_acm*: `.mus`
|
||||
- Subfiles: *acm ogg_vorbis*
|
||||
- **vig_kces.c**
|
||||
- Konami .VIG Header [*VIG_KCES*]
|
||||
- Konami .VIG header [*VIG_KCES*]
|
||||
- *vig_kces*: `.vig`
|
||||
- Codecs: PSX
|
||||
- **hxd.c**
|
||||
- Tecmo HXD Header [*HXD*]
|
||||
- Tecmo HXD header [*HXD*]
|
||||
- *hxd*: `.hxd + .bd .str .at3`
|
||||
- Codecs: PSX
|
||||
- **vsv.c**
|
||||
@ -466,11 +455,11 @@ different internally (encrypted, different versions, etc) and not always can be
|
||||
- *kraw*: `.kraw`
|
||||
- Codecs: PCM16BE
|
||||
- **omu.c**
|
||||
- Outrage OMU Header [*OMU*]
|
||||
- Outrage OMU header [*OMU*]
|
||||
- *omu*: `.omu`
|
||||
- Codecs: PCM16LE
|
||||
- **xa2_acclaim.c**
|
||||
- Acclaim .XA2 Header [*XA2_ACCLAIM*]
|
||||
- Acclaim .XA2 header [*XA2_ACCLAIM*]
|
||||
- *xa2_acclaim*: `.xa2`
|
||||
- Codecs: PSX
|
||||
- **idsp_ie.c**
|
||||
@ -642,7 +631,7 @@ different internally (encrypted, different versions, etc) and not always can be
|
||||
- **ads_midway.c**
|
||||
- Midway ADS header [*ADS_MIDWAY*]
|
||||
- *ads_midway*: `.ads`
|
||||
- Codecs: NGC_DSP XBOX_IMA_int
|
||||
- Codecs: NGC_DSP XBOX_IMA_mono
|
||||
- **ps2_mcg.c**
|
||||
- Gunvari MCG Header [*PS2_MCG*]
|
||||
- *ps2_mcg*: `.mcg`
|
||||
@ -1075,6 +1064,22 @@ different internally (encrypted, different versions, etc) and not always can be
|
||||
- Reflections 04SW header [*XA_04SW*]
|
||||
- *xa_04sw*: `.xa`
|
||||
- Codecs: NGC_DSP
|
||||
- **ea_schl_abk.c**
|
||||
- (container)
|
||||
- *ea_abk_schl*: `.abk`
|
||||
- *ea_amb_schl*: `.amb .amx`
|
||||
- *ea_abk_schl_main*: `(base) + .ast`
|
||||
- **ea_schl_hdr_dat.c**
|
||||
- (container)
|
||||
- *ea_hdr_dat*: `.hdr + .dat`
|
||||
- Subfiles: *vag*
|
||||
- *ea_hdr_dat_v2*: `.hdr + .dat`
|
||||
- **ea_schl_map_mpf_mus.c**
|
||||
- (container)
|
||||
- *ea_map_mus*: `.map .lin .mpf`
|
||||
- *ea_mpf_mus_schl*: `.mpf`
|
||||
- *ea_msb_mus_schl*: `.msb .msx`
|
||||
- *ea_mpf_mus_schl_main*: `(base) + .(external) .mus`
|
||||
- **ea_schl_fixed.c**
|
||||
- Electronic Arts SCHl header (fixed) [*EA_SCHL_fixed*]
|
||||
- *ea_schl_fixed*: `.asf .lasf .cnk`
|
||||
@ -1148,7 +1153,9 @@ different internally (encrypted, different versions, etc) and not always can be
|
||||
- Codecs: PCM16LE MSADPCM MS_IMA FFmpeg(various)
|
||||
- **ea_eaac_abk.c**
|
||||
- Electronic Arts SNR+SNS header [*EA_SNR_SNS*]
|
||||
- *ea_abk_eaac*: `.abk + .ast`
|
||||
- *ea_abk_eaac*: `.abk`
|
||||
- *ea_amb_eaac*: `.amb .amx`
|
||||
- *ea_abk_eaac_main*: `(base) + .ast`
|
||||
- **ea_eaac_hdr_sth_dat.c**
|
||||
- Electronic Arts SNR+SNS header [*EA_SNR_SNS*]
|
||||
- *ea_hdr_sth_dat*: `.hdr + .sth .dat`
|
||||
@ -1242,7 +1249,7 @@ different internally (encrypted, different versions, etc) and not always can be
|
||||
- **sthd.c**
|
||||
- Dream Factory STHD header [*STHD*]
|
||||
- *sthd*: `.stx`
|
||||
- Codecs: PCM16LE XBOX_IMA_int
|
||||
- Codecs: PCM16LE XBOX_IMA_mono
|
||||
- **pcm_sre.c**
|
||||
- Capcom .PCM+SRE header [*PCM_SRE*]
|
||||
- *pcm_sre*: `.pcm + .sre`
|
||||
@ -1621,7 +1628,7 @@ different internally (encrypted, different versions, etc) and not always can be
|
||||
- *asrs*: `.srsa`
|
||||
- *ktsr_internal*
|
||||
- Subfiles: *riff ogg_vorbis ktss ktac*
|
||||
- Codecs: MSADPCM_int NGC_DSP ATRAC9
|
||||
- Codecs: MSADPCM_mono NGC_DSP ATRAC9
|
||||
- **mups.c**
|
||||
- (container)
|
||||
- *mups*: `.mups .(extensionless)`
|
||||
@ -1833,6 +1840,10 @@ different internally (encrypted, different versions, etc) and not always can be
|
||||
- Rebellion DSP header [*DSP_ASURA*]
|
||||
- *dsp_asura_sfx*: `.sfx`
|
||||
- Codecs: NGC_DSP
|
||||
- **adp_ongakukan.c**
|
||||
- Ongakukan RIFF WAVE header [*ONGAKUKAN_RIFF_ADP*]
|
||||
- *adp_ongakukan*: `.adp`
|
||||
- Codecs: ONGAKUKAN_ADPCM
|
||||
- **agsc.c**
|
||||
- Retro Studios AGSC header [*AGSC*]
|
||||
- *agsc*: `.agsc`
|
||||
@ -1957,6 +1968,10 @@ different internally (encrypted, different versions, etc) and not always can be
|
||||
- Electronic Arts SPS header [*EA_SPS*]
|
||||
- *eaaudiocore_main*: `(base) + .sns`
|
||||
- Codecs: PCM16_int EA_XAS_V1 MPEG NGC_DSP SPEEX ATRAC9 Opus XMA1 XMA2
|
||||
- **ea_schl.c**
|
||||
- Electronic Arts BNK header [*EA_BNK*]
|
||||
- Electronic Arts SCHl header [*EA_SCHL*]
|
||||
- Codecs: EA_XA EA_XA_int EA_XA_V2 PCM8_int PCM16_int PCM8 PCM16LE PCM16BE PSX XBOX_IMA_mono NGC_DSP VADPCM MPEG EA_MT ATRAC3
|
||||
|
||||
## Supported extras
|
||||
Reminder of some extra formats and helper files vgmstream supports. They are described
|
||||
|
10
src/Makefile
10
src/Makefile
@ -2,13 +2,15 @@
|
||||
# main vgmstream code
|
||||
#
|
||||
|
||||
# automatically get all possible .o by finding all .c
|
||||
OBJECTS =
|
||||
|
||||
#SRCS = $(wildcard **/*.c) #GNUMake 3.81?
|
||||
SRCS = $(wildcard *.c) $(wildcard */*.c) $(wildcard */*/*.c)
|
||||
|
||||
# get all possible .o by finding all .c
|
||||
OBJECTS = $(patsubst %.c,%.o,$(SRCS))
|
||||
|
||||
# in case of renamed files
|
||||
OBJECTS_CLEAN = $(wildcard *.o) $(wildcard */*.o) $(wildcard */*/*.o)
|
||||
|
||||
|
||||
libvgmstream.a: $(OBJECTS)
|
||||
$(AR) crs libvgmstream.a $(OBJECTS)
|
||||
@ -20,6 +22,6 @@ libvgmstream.so: $(OBJECTS)
|
||||
# $(CC) $(CFLAGS) -M -o vgmstream-deps
|
||||
|
||||
clean:
|
||||
$(RMF) $(OBJECTS) libvgmstream.a
|
||||
$(RMF) $(OBJECTS_CLEAN) libvgmstream.a
|
||||
|
||||
.PHONY: clean
|
||||
|
@ -42,9 +42,9 @@ typedef struct libvgmstream_streamfile_t {
|
||||
*/
|
||||
int64_t (*get_size)(void* user_data);
|
||||
|
||||
/* copy current filename to name buf
|
||||
/* get current filename
|
||||
*/
|
||||
void (*get_name)(void* user_data, char* name, int name_size); //TODO return char*?
|
||||
const char* (*get_name)(void* user_data);
|
||||
|
||||
/* open another streamfile from filename (may be some path/protocol, or same as current get_name = reopen)
|
||||
* - vgmstream mainly opens stuff based on current get_name (relative), so there shouldn't be need to transform this path
|
||||
|
@ -9,6 +9,7 @@ typedef struct {
|
||||
int64_t offset;
|
||||
int64_t size;
|
||||
STREAMFILE* inner_sf;
|
||||
char name[PATH_LIMIT];
|
||||
} libsf_data_t;
|
||||
|
||||
static int libsf_read(void* user_data, uint8_t* dst, int dst_size) {
|
||||
@ -58,16 +59,16 @@ static int64_t libsf_get_size(void* user_data) {
|
||||
return data->size;
|
||||
}
|
||||
|
||||
static void libsf_get_name(void* user_data, char* name, int name_size) {
|
||||
if (!name || !name_size)
|
||||
return;
|
||||
name[0] = '\0';
|
||||
|
||||
static const char* libsf_get_name(void* user_data) {
|
||||
libsf_data_t* data = user_data;
|
||||
if (!data)
|
||||
return;
|
||||
return NULL;
|
||||
|
||||
data->inner_sf->get_name(data->inner_sf, name, name_size); /* default */
|
||||
if (data->name[0] == '\0') {
|
||||
data->inner_sf->get_name(data->inner_sf, data->name, sizeof(data->name));
|
||||
}
|
||||
|
||||
return data->name;
|
||||
}
|
||||
|
||||
struct libvgmstream_streamfile_t* libsf_open(void* user_data, const char* filename) {
|
||||
|
@ -33,7 +33,16 @@ static offv_t api_get_offset(API_STREAMFILE* sf) {
|
||||
static void api_get_name(API_STREAMFILE* sf, char* name, size_t name_size) {
|
||||
void* user_data = sf->libsf->user_data;
|
||||
|
||||
sf->libsf->get_name(user_data, name, name_size);
|
||||
if (!name || !name_size)
|
||||
return;
|
||||
name[0] = '\0';
|
||||
|
||||
const char* external_name = sf->libsf->get_name(user_data);
|
||||
if (!external_name)
|
||||
return;
|
||||
|
||||
snprintf(name, name_size, "%s", external_name);
|
||||
name[name_size - 1] = '\0';
|
||||
}
|
||||
|
||||
static STREAMFILE* api_open(API_STREAMFILE* sf, const char* filename, size_t buf_size) {
|
||||
|
@ -1,221 +1,223 @@
|
||||
|
||||
/* Decodes Ongakukan ADPCM, found in their PS2 and PSP games.
|
||||
* Basically their take on ADPCM with some companding and quantization involved.
|
||||
*
|
||||
* Original decoder is a mix of COP0 and VU1 code, however PS2 floats aren't actually used (if at all)
|
||||
* when it comes to converting encoded sample data (consisting of a single byte with two 4-bit nibbles, respectively) to PCM16.
|
||||
*
|
||||
* The decoder you see here is a hand-crafted, faithful C adaptation of original MIPS R5900 (PS2) and R4000 (PSP) code, from various executables of their games.
|
||||
* As a consequence of all this, a new, entirely custom decoder had to be designed from the ground-up into vgmstream. No info surrounding this codec was available. */
|
||||
|
||||
/* Additional notes:
|
||||
* - This code does not support PCM16 sound data, in any way, shape, or form.
|
||||
* -- Ongakukan's internal sound engine from their PS2 and PSP games allow for only two codecs: signed PCM16, and their own take on ADPCM, respectively.
|
||||
* -- However, much of that support is reliant on a flag that's set to either one of the two codecs depending on the opened file extension.
|
||||
* Basically, how it works is: if sound data is "PCM16" (available to "wav" and "ads" files), set flag to 0.
|
||||
* If sound data is "ADPCM" (available to "adp" files), set it to 1.
|
||||
* Code handles this flag as a boolean var; 0 is "false" and 1 is "true".
|
||||
* -- As vgmstream has built-in support for the former codec (and the many metas that use it) however, despite being fairly easy to add here,
|
||||
* re-implementing one from scratch would be a wasted effort regardless; it is consequentially not included. */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "../../util/reader_sf.h"
|
||||
#include "ongakukan_adp_lib.h"
|
||||
|
||||
/* the struct that oversees everything. */
|
||||
|
||||
struct ongakukan_adp_t
|
||||
{
|
||||
STREAMFILE* sf; /* streamfile var. */
|
||||
|
||||
long int data_offset; /* current offset of data that's being read. */
|
||||
long int start_offset; /* base offset of encoded sound data. */
|
||||
long int data_size; /* sound data size, basically ADP size if it didn't have 44 bytes more. */
|
||||
long int sample_work; /* total number of samples, calc'd using data_size as a base. */
|
||||
long int alt_sample_work1; /* represents current number of samples as they're decoded. */
|
||||
long int alt_sample_work2; /* represents the many samples left to go through. */
|
||||
long int samples_filled; /* how many samples were filled to vgmstream buffer. */
|
||||
long int samples_consumed; /* how many samples vgmstream buffer had to consume. */
|
||||
|
||||
bool sound_is_adpcm; /* false = no (see "additional notes" above) , true = yes */
|
||||
bool sample_startpoint_present; /* false = failed to make startpoint, true = startpoint present */
|
||||
char sample_mode; /* 0 = creates decoding setup, 1 = continue decoding data with setup in place */
|
||||
bool sample_pair_is_decoded; /* false = no, true = yes */
|
||||
|
||||
unsigned char base_pair; /* represents a read byte from ADPCM data, consisting of two 4-bit nibbles each.*/
|
||||
long int base_scale; /* how loud should this sample be. */
|
||||
short int sample_hist[2]; /* two pairs of signed 16-bit data, representing samples. yes, it is void. */
|
||||
};
|
||||
|
||||
/* filter table consisting of 16 numbers each. */
|
||||
|
||||
const short int ongakukan_adpcm_filter[16] = { 233, 549, 453, 375, 310, 233, 233, 233, 233, 233, 233, 233, 310, 375, 453, 549 };
|
||||
|
||||
/* streamfile read function declararion, more may be added in the future. */
|
||||
|
||||
static uint8_t read_u8_wrapper(ongakukan_adp_t* handle);
|
||||
|
||||
/* function declarations for the inner workings of codec data. */
|
||||
|
||||
static bool set_up_sample_startpoint(ongakukan_adp_t* handle);
|
||||
static void decode_ongakukan_adpcm_samples(ongakukan_adp_t* handle);
|
||||
|
||||
/* codec management functions, meant to oversee and supervise ADP data from the top-down.
|
||||
* in layman terms, they control how ADP data should be handled and when. */
|
||||
|
||||
ongakukan_adp_t* init_ongakukan_adpcm(STREAMFILE* sf, long int data_offset, long int data_size,
|
||||
bool sound_is_adpcm)
|
||||
{
|
||||
ongakukan_adp_t* handle = NULL;
|
||||
|
||||
/* allocate handle using malloc. */
|
||||
handle = malloc(sizeof(ongakukan_adp_t));
|
||||
if (!handle) goto fail;
|
||||
|
||||
/* now, to set up the rest of the handle with the data we have... */
|
||||
handle->sf = sf;
|
||||
handle->data_offset = data_offset;
|
||||
handle->start_offset = data_offset;
|
||||
handle->data_size = data_size;
|
||||
handle->sample_mode = 0;
|
||||
handle->sound_is_adpcm = sound_is_adpcm;
|
||||
handle->sample_startpoint_present = set_up_sample_startpoint(handle);
|
||||
/* if we failed in planting up the seeds for an ADPCM decoder, we simply throw in the towel and take a walk in the park. */
|
||||
if (handle->sample_startpoint_present == false) { goto fail; }
|
||||
|
||||
return handle;
|
||||
fail:
|
||||
ongakukan_adpcm_free(handle);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void ongakukan_adpcm_free(ongakukan_adp_t* handle)
|
||||
{
|
||||
if (!handle) return;
|
||||
free(handle);
|
||||
}
|
||||
|
||||
void ongakukan_adpcm_reset(ongakukan_adp_t* handle)
|
||||
{
|
||||
if (!handle) return;
|
||||
|
||||
/* wipe out certain values from handle so we can start over. */
|
||||
handle->data_offset = handle->start_offset;
|
||||
handle->sample_pair_is_decoded = false;
|
||||
handle->sample_mode = 0;
|
||||
handle->alt_sample_work1 = 0;
|
||||
handle->alt_sample_work2 = handle->sample_work;
|
||||
}
|
||||
|
||||
void ongakukan_adpcm_seek(ongakukan_adp_t* handle, long int target_sample)
|
||||
{
|
||||
if (!handle) return;
|
||||
|
||||
char ts_modulus = 0; /* ts_modulus is here to ensure target_sample gets rounded to a multiple of 2. */
|
||||
long int ts_data_offset = 0; /* ts_data_offset is basically data_offset but with (left(if PCM)/right(if ADPCM))-shifted target_sample calc by 1. */
|
||||
ts_data_offset = target_sample >> 1;
|
||||
ts_modulus = target_sample % 2;
|
||||
target_sample = target_sample - ts_modulus;
|
||||
/* if ADPCM, right-shift the former first then have ts_modulus calc remainder of target_sample by 2 so we can subtract it with ts_modulus.
|
||||
* this is needed for the two counters that the decoder has that can both add and subtract with 2, respectively
|
||||
* (and in order, too; meaning one counter does "plus 2" while the other does "minus 2",
|
||||
* and though they're fairly useless ATM, you pretty much want to leave them alone). */
|
||||
|
||||
/* anyway, we'll have to tell decoder that target_sample is calling and wants to go somewhere right now,
|
||||
* so we'll have data_offset reposition itself to where sound data for that sample ought to be
|
||||
* and (as of now) reset basically all decode state up to this point so we can continue to decode all sample pairs without issue. */
|
||||
handle->data_offset = handle->start_offset + ts_data_offset;
|
||||
handle->sample_pair_is_decoded = false;
|
||||
handle->sample_mode = 0;
|
||||
handle->alt_sample_work1 = target_sample;
|
||||
handle->alt_sample_work2 = handle->sample_work - target_sample;
|
||||
|
||||
/* for now, just do what reset_all_ongakukan_adpcm does but for the current sample instead of literally everything.
|
||||
* seek_ongakukan_adpcm_pos in its current state is a bit more involved than the above, but works. */
|
||||
}
|
||||
|
||||
long int get_num_samples_from_ongakukan_adpcm(ongakukan_adp_t* handle)
|
||||
{
|
||||
if (!handle) return 0;
|
||||
return handle->sample_work;
|
||||
}
|
||||
|
||||
void* get_sample_hist_from_ongakukan_adpcm(ongakukan_adp_t* handle)
|
||||
{
|
||||
if (!handle) return 0;
|
||||
return &handle->sample_hist;
|
||||
}
|
||||
|
||||
/* function definitions for the inner workings of codec data. */
|
||||
|
||||
static bool set_up_sample_startpoint(ongakukan_adp_t* handle)
|
||||
{
|
||||
/* make decoder fail hard if streamfile object isn't opened or downright useless. */
|
||||
if (!handle->sf) return false;
|
||||
|
||||
if (handle->sound_is_adpcm == 0) { return false; }
|
||||
else { /* num_samples but for Ongakukan ADPCM sound data. */ handle->sample_work = handle->data_size << 1; }
|
||||
/* set "beginning" and "end" sample vars and send a "message" that we went through no sample yet.*/
|
||||
handle->alt_sample_work1 = 0;
|
||||
handle->alt_sample_work2 = handle->sample_work;
|
||||
handle->sample_pair_is_decoded = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void decode_ongakukan_adpcm_data(ongakukan_adp_t* handle)
|
||||
{
|
||||
/* set samples_filled to 0 and have our decoder go through every sample that exists in the sound data.*/
|
||||
decode_ongakukan_adpcm_samples(handle);
|
||||
/* if setup is established for further decoding, switch gears and have the decoder use that setup for as long as possible. */
|
||||
/* if sample pair is decoded, advance to next byte, tell our handle that we went through 2 samples and make decoder go through next available data again. */
|
||||
if (handle->sample_pair_is_decoded == true)
|
||||
{
|
||||
handle->data_offset++;
|
||||
handle->alt_sample_work1 += 2;
|
||||
handle->alt_sample_work2 -= 2;
|
||||
handle->sample_pair_is_decoded = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void decode_ongakukan_adpcm_samples(ongakukan_adp_t* handle)
|
||||
{
|
||||
unsigned char nibble1 = 0, nibble2 = 0; /* two chars representing a 4-bit nibble. */
|
||||
long int nibble1_1 = 0, nibble2_1 = 0; /* two long ints representing pure sample data. */
|
||||
|
||||
if (handle->sample_pair_is_decoded == false)
|
||||
{
|
||||
/* sample_mode being 0 means we can just do a setup for future sample decoding so we have nothing to worry about in the future. */
|
||||
if (handle->sample_mode == 0)
|
||||
{
|
||||
/* set "base scale", two "sample hist"s, and "base pair", respectively. */
|
||||
handle->base_scale = 0x10;
|
||||
handle->sample_hist[0] = 0;
|
||||
handle->sample_hist[1] = 0;
|
||||
handle->base_pair = 0;
|
||||
handle->sample_mode = 1; /* indicates we have the setup we need to decode samples. */
|
||||
}
|
||||
handle->base_pair = (uint8_t)read_u8_wrapper(handle);
|
||||
|
||||
nibble1 = handle->base_pair & 0xf;
|
||||
nibble1_1 = nibble1 + -8;
|
||||
nibble2 = (handle->base_pair >> 4) & 0xf;
|
||||
nibble2_1 = nibble2 + -8;
|
||||
nibble2_1 = nibble2_1 * handle->base_scale;
|
||||
handle->sample_hist[0] = handle->sample_hist[1] + nibble2_1;
|
||||
handle->base_scale = (handle->base_scale * (ongakukan_adpcm_filter[nibble2])) >> 8;
|
||||
nibble1_1 = nibble1_1 * handle->base_scale;
|
||||
handle->sample_hist[1] = handle->sample_hist[0] + nibble1_1;
|
||||
handle->base_scale = (handle->base_scale * (ongakukan_adpcm_filter[nibble1])) >> 8;
|
||||
handle->sample_pair_is_decoded = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* streamfile read function definitions at the very bottom. */
|
||||
|
||||
static uint8_t read_u8_wrapper(ongakukan_adp_t* handle)
|
||||
{
|
||||
if ((handle->data_offset - handle->start_offset) > handle->data_size) return 0;
|
||||
if ((handle->data_offset - handle->start_offset) < 0) return 0;
|
||||
return read_u8((off_t)(handle->data_offset), handle->sf);
|
||||
}
|
||||
|
||||
/* Decodes Ongakukan ADPCM, found in their PS2 and PSP games.
|
||||
* Basically their take on ADPCM with some companding and quantization involved.
|
||||
*
|
||||
* Original decoder is a mix of COP0 and VU1 code, however PS2 floats aren't actually used (if at all)
|
||||
* when it comes to converting encoded sample data (consisting of a single byte with two 4-bit nibbles, respectively) to PCM16.
|
||||
*
|
||||
* The decoder you see here is a hand-crafted, faithful C adaptation of original MIPS R5900 (PS2) and R4000 (PSP) code, from various executables of their games.
|
||||
* As a consequence of all this, a new, entirely custom decoder had to be designed from the ground-up into vgmstream. No info surrounding this codec was available. */
|
||||
|
||||
/* Additional notes:
|
||||
* - This code does not support PCM16 sound data, in any way, shape, or form.
|
||||
* -- Ongakukan's internal sound engine from their PS2 and PSP games allow for only two codecs: signed PCM16, and their own take on ADPCM, respectively.
|
||||
* -- However, much of that support is reliant on a flag that's set to either one of the two codecs depending on the opened file extension.
|
||||
* Basically, how it works is: if sound data is "PCM16" (available to "wav" and "ads" files), set flag to 0.
|
||||
* If sound data is "ADPCM" (available to "adp" files), set it to 1.
|
||||
* Code handles this flag as a boolean var; 0 is "false" and 1 is "true".
|
||||
* -- As vgmstream has built-in support for the former codec (and the many metas that use it) however, despite being fairly easy to add here,
|
||||
* re-implementing one from scratch would be a wasted effort regardless; it is consequentially not included. */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "../../util/reader_sf.h"
|
||||
#include "ongakukan_adp_lib.h"
|
||||
|
||||
/* the struct that oversees everything. */
|
||||
|
||||
struct ongakukan_adp_t
|
||||
{
|
||||
STREAMFILE* sf; /* streamfile var. */
|
||||
|
||||
long int data_offset; /* current offset of data that's being read. */
|
||||
long int start_offset; /* base offset of encoded sound data. */
|
||||
long int data_size; /* sound data size, basically ADP size if it didn't have 44 bytes more. */
|
||||
long int sample_work; /* total number of samples, calc'd using data_size as a base. */
|
||||
long int alt_sample_work1; /* represents current number of samples as they're decoded. */
|
||||
long int alt_sample_work2; /* represents the many samples left to go through. */
|
||||
long int samples_filled; /* how many samples were filled to vgmstream buffer. */
|
||||
long int samples_consumed; /* how many samples vgmstream buffer had to consume. */
|
||||
|
||||
bool sound_is_adpcm; /* false = no (see "additional notes" above) , true = yes */
|
||||
bool sample_startpoint_present; /* false = failed to make startpoint, true = startpoint present */
|
||||
char sample_mode; /* 0 = creates decoding setup, 1 = continue decoding data with setup in place */
|
||||
bool sample_pair_is_decoded; /* false = no, true = yes */
|
||||
|
||||
unsigned char base_pair; /* represents a read byte from ADPCM data, consisting of two 4-bit nibbles each.*/
|
||||
long int base_scale; /* how loud should this sample be. */
|
||||
short int sample_hist[2]; /* two pairs of signed 16-bit data, representing samples. yes, it is void. */
|
||||
};
|
||||
|
||||
/* filter table consisting of 16 numbers each. */
|
||||
|
||||
static const short int ongakukan_adpcm_filter[16] = { 233, 549, 453, 375, 310, 233, 233, 233, 233, 233, 233, 233, 310, 375, 453, 549 };
|
||||
|
||||
/* streamfile read function declararion, more may be added in the future. */
|
||||
|
||||
static uint8_t read_u8_wrapper(ongakukan_adp_t* handle);
|
||||
|
||||
/* function declarations for the inner workings of codec data. */
|
||||
|
||||
static bool set_up_sample_startpoint(ongakukan_adp_t* handle);
|
||||
static void decode_ongakukan_adpcm_samples(ongakukan_adp_t* handle);
|
||||
|
||||
/* codec management functions, meant to oversee and supervise ADP data from the top-down.
|
||||
* in layman terms, they control how ADP data should be handled and when. */
|
||||
|
||||
ongakukan_adp_t* ongakukan_adpcm_init(STREAMFILE* sf, long int data_offset, long int data_size, bool sound_is_adpcm)
|
||||
{
|
||||
ongakukan_adp_t* handle = NULL;
|
||||
|
||||
if (!sound_is_adpcm)
|
||||
return NULL;
|
||||
|
||||
/* allocate handle. */
|
||||
handle = calloc(1, sizeof(ongakukan_adp_t));
|
||||
if (!handle) goto fail;
|
||||
|
||||
/* now, to set up the rest of the handle with the data we have... */
|
||||
handle->sf = sf;
|
||||
handle->data_offset = data_offset;
|
||||
handle->start_offset = data_offset;
|
||||
handle->data_size = data_size;
|
||||
handle->sample_mode = 0;
|
||||
handle->sound_is_adpcm = sound_is_adpcm;
|
||||
handle->sample_startpoint_present = set_up_sample_startpoint(handle);
|
||||
/* if we failed in planting up the seeds for an ADPCM decoder, we simply throw in the towel and take a walk in the park. */
|
||||
if (handle->sample_startpoint_present == false) { goto fail; }
|
||||
|
||||
return handle;
|
||||
fail:
|
||||
ongakukan_adpcm_free(handle);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void ongakukan_adpcm_free(ongakukan_adp_t* handle)
|
||||
{
|
||||
if (!handle) return;
|
||||
free(handle);
|
||||
}
|
||||
|
||||
void ongakukan_adpcm_reset(ongakukan_adp_t* handle)
|
||||
{
|
||||
if (!handle) return;
|
||||
|
||||
/* wipe out certain values from handle so we can start over. */
|
||||
handle->data_offset = handle->start_offset;
|
||||
handle->sample_pair_is_decoded = false;
|
||||
handle->sample_mode = 0;
|
||||
handle->alt_sample_work1 = 0;
|
||||
handle->alt_sample_work2 = handle->sample_work;
|
||||
}
|
||||
|
||||
void ongakukan_adpcm_seek(ongakukan_adp_t* handle, long int target_sample)
|
||||
{
|
||||
if (!handle) return;
|
||||
|
||||
char ts_modulus = 0; /* ts_modulus is here to ensure target_sample gets rounded to a multiple of 2. */
|
||||
long int ts_data_offset = 0; /* ts_data_offset is basically data_offset but with (left(if PCM)/right(if ADPCM))-shifted target_sample calc by 1. */
|
||||
ts_data_offset = target_sample >> 1;
|
||||
ts_modulus = target_sample % 2;
|
||||
target_sample = target_sample - ts_modulus;
|
||||
/* if ADPCM, right-shift the former first then have ts_modulus calc remainder of target_sample by 2 so we can subtract it with ts_modulus.
|
||||
* this is needed for the two counters that the decoder has that can both add and subtract with 2, respectively
|
||||
* (and in order, too; meaning one counter does "plus 2" while the other does "minus 2",
|
||||
* and though they're fairly useless ATM, you pretty much want to leave them alone). */
|
||||
|
||||
/* anyway, we'll have to tell decoder that target_sample is calling and wants to go somewhere right now,
|
||||
* so we'll have data_offset reposition itself to where sound data for that sample ought to be
|
||||
* and (as of now) reset basically all decode state up to this point so we can continue to decode all sample pairs without issue. */
|
||||
handle->data_offset = handle->start_offset + ts_data_offset;
|
||||
handle->sample_pair_is_decoded = false;
|
||||
handle->sample_mode = 0;
|
||||
handle->alt_sample_work1 = target_sample;
|
||||
handle->alt_sample_work2 = handle->sample_work - target_sample;
|
||||
|
||||
/* for now, just do what reset_all_ongakukan_adpcm does but for the current sample instead of literally everything.
|
||||
* seek_ongakukan_adpcm_pos in its current state is a bit more involved than the above, but works. */
|
||||
}
|
||||
|
||||
long int ongakukan_adpcm_get_num_samples(ongakukan_adp_t* handle)
|
||||
{
|
||||
if (!handle) return 0;
|
||||
return handle->sample_work;
|
||||
}
|
||||
|
||||
short* ongakukan_adpcm_get_sample_hist(ongakukan_adp_t* handle)
|
||||
{
|
||||
if (!handle) return 0;
|
||||
return handle->sample_hist;
|
||||
}
|
||||
|
||||
/* function definitions for the inner workings of codec data. */
|
||||
|
||||
static bool set_up_sample_startpoint(ongakukan_adp_t* handle)
|
||||
{
|
||||
/* make decoder fail hard if streamfile object isn't opened or downright useless. */
|
||||
if (!handle->sf) return false;
|
||||
|
||||
if (handle->sound_is_adpcm == 0) { return false; }
|
||||
else { /* num_samples but for Ongakukan ADPCM sound data. */ handle->sample_work = handle->data_size << 1; }
|
||||
/* set "beginning" and "end" sample vars and send a "message" that we went through no sample yet.*/
|
||||
handle->alt_sample_work1 = 0;
|
||||
handle->alt_sample_work2 = handle->sample_work;
|
||||
handle->sample_pair_is_decoded = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ongakukan_adpcm_decode_data(ongakukan_adp_t* handle)
|
||||
{
|
||||
/* set samples_filled to 0 and have our decoder go through every sample that exists in the sound data.*/
|
||||
decode_ongakukan_adpcm_samples(handle);
|
||||
/* if setup is established for further decoding, switch gears and have the decoder use that setup for as long as possible. */
|
||||
/* if sample pair is decoded, advance to next byte, tell our handle that we went through 2 samples and make decoder go through next available data again. */
|
||||
if (handle->sample_pair_is_decoded == true)
|
||||
{
|
||||
handle->data_offset++;
|
||||
handle->alt_sample_work1 += 2;
|
||||
handle->alt_sample_work2 -= 2;
|
||||
handle->sample_pair_is_decoded = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void decode_ongakukan_adpcm_samples(ongakukan_adp_t* handle)
|
||||
{
|
||||
unsigned char nibble1 = 0, nibble2 = 0; /* two chars representing a 4-bit nibble. */
|
||||
long int nibble1_1 = 0, nibble2_1 = 0; /* two long ints representing pure sample data. */
|
||||
|
||||
if (handle->sample_pair_is_decoded == false)
|
||||
{
|
||||
/* sample_mode being 0 means we can just do a setup for future sample decoding so we have nothing to worry about in the future. */
|
||||
if (handle->sample_mode == 0)
|
||||
{
|
||||
/* set "base scale", two "sample hist"s, and "base pair", respectively. */
|
||||
handle->base_scale = 0x10;
|
||||
handle->sample_hist[0] = 0;
|
||||
handle->sample_hist[1] = 0;
|
||||
handle->base_pair = 0;
|
||||
handle->sample_mode = 1; /* indicates we have the setup we need to decode samples. */
|
||||
}
|
||||
handle->base_pair = (uint8_t)read_u8_wrapper(handle);
|
||||
|
||||
nibble1 = handle->base_pair & 0xf;
|
||||
nibble1_1 = nibble1 + -8;
|
||||
nibble2 = (handle->base_pair >> 4) & 0xf;
|
||||
nibble2_1 = nibble2 + -8;
|
||||
nibble2_1 = nibble2_1 * handle->base_scale;
|
||||
handle->sample_hist[0] = handle->sample_hist[1] + nibble2_1;
|
||||
handle->base_scale = (handle->base_scale * (ongakukan_adpcm_filter[nibble2])) >> 8;
|
||||
nibble1_1 = nibble1_1 * handle->base_scale;
|
||||
handle->sample_hist[1] = handle->sample_hist[0] + nibble1_1;
|
||||
handle->base_scale = (handle->base_scale * (ongakukan_adpcm_filter[nibble1])) >> 8;
|
||||
handle->sample_pair_is_decoded = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* streamfile read function definitions at the very bottom. */
|
||||
|
||||
static uint8_t read_u8_wrapper(ongakukan_adp_t* handle)
|
||||
{
|
||||
if ((handle->data_offset - handle->start_offset) > handle->data_size) return 0;
|
||||
if ((handle->data_offset - handle->start_offset) < 0) return 0;
|
||||
return read_u8((off_t)(handle->data_offset), handle->sf);
|
||||
}
|
||||
|
@ -1,27 +1,27 @@
|
||||
#ifndef _ONGAKUKAN_ADP_LIB_H_
|
||||
#define _ONGAKUKAN_ADP_LIB_H_
|
||||
|
||||
/* Ongakukan ADPCM codec, found in PS2 and PSP games. */
|
||||
|
||||
#include "../../util/reader_sf.h"
|
||||
|
||||
/* typedef struct */
|
||||
typedef struct ongakukan_adp_t ongakukan_adp_t;
|
||||
|
||||
/* function declaration for we need to set up the codec data. */
|
||||
ongakukan_adp_t* init_ongakukan_adpcm(STREAMFILE* sf, long int data_offset, long int data_size,
|
||||
bool sound_is_adpcm);
|
||||
|
||||
/* function declaration for freeing all memory related to ongakukan_adp_t struct var. */
|
||||
void ongakukan_adpcm_free(ongakukan_adp_t* handle);
|
||||
void ongakukan_adpcm_reset(ongakukan_adp_t* handle);
|
||||
void ongakukan_adpcm_seek(ongakukan_adp_t* handle, long int target_sample);
|
||||
|
||||
/* function declaration for when we need to get (and send) certain values from ongakukan_adp_t handle */
|
||||
long int get_num_samples_from_ongakukan_adpcm(ongakukan_adp_t* handle);
|
||||
void* get_sample_hist_from_ongakukan_adpcm(ongakukan_adp_t* handle);
|
||||
|
||||
/* function declaration for actually decoding samples, can't be that hard, right? */
|
||||
void decode_ongakukan_adpcm_data(ongakukan_adp_t* handle);
|
||||
|
||||
#endif // _ONGAKUKAN_ADP_LIB_H_
|
||||
#define _ONGAKUKAN_ADP_LIB_H_
|
||||
|
||||
/* Ongakukan ADPCM codec, found in PS2 and PSP games. */
|
||||
|
||||
#include "../../util/reader_sf.h"
|
||||
|
||||
/* typedef struct */
|
||||
typedef struct ongakukan_adp_t ongakukan_adp_t;
|
||||
|
||||
/* function declaration for we need to set up the codec data. */
|
||||
ongakukan_adp_t* ongakukan_adpcm_init(STREAMFILE* sf, long int data_offset, long int data_size,
|
||||
bool sound_is_adpcm);
|
||||
|
||||
/* function declaration for freeing all memory related to ongakukan_adp_t struct var. */
|
||||
void ongakukan_adpcm_free(ongakukan_adp_t* handle);
|
||||
void ongakukan_adpcm_reset(ongakukan_adp_t* handle);
|
||||
void ongakukan_adpcm_seek(ongakukan_adp_t* handle, long int target_sample);
|
||||
|
||||
/* function declaration for when we need to get (and send) certain values from ongakukan_adp_t handle */
|
||||
long int ongakukan_adpcm_get_num_samples(ongakukan_adp_t* handle);
|
||||
short* ongakukan_adpcm_get_sample_hist(ongakukan_adp_t* handle);
|
||||
|
||||
/* function declaration for actually decoding samples, can't be that hard, right? */
|
||||
void ongakukan_adpcm_decode_data(ongakukan_adp_t* handle);
|
||||
|
||||
#endif
|
||||
|
@ -1,89 +1,76 @@
|
||||
#include <stdlib.h>
|
||||
#include "coding.h"
|
||||
#include "libs/ongakukan_adp_lib.h"
|
||||
|
||||
struct ongakukan_adp_data
|
||||
{
|
||||
void* handle;
|
||||
int16_t* samples;
|
||||
int32_t samples_done;
|
||||
bool samples_filled; /* false - no, true - yes */
|
||||
int32_t getting_samples; /* initialized to 2 on decode_ongakukan_adp. i mean, we literally get two decoded samples here. */
|
||||
STREAMFILE* sf;
|
||||
};
|
||||
|
||||
ongakukan_adp_data* init_ongakukan_adp(STREAMFILE* sf, int32_t data_offset, int32_t data_size,
|
||||
bool sound_is_adpcm)
|
||||
{
|
||||
ongakukan_adp_data* data = NULL;
|
||||
|
||||
data = calloc(1, sizeof(ongakukan_adp_data));
|
||||
if (!data) goto fail;
|
||||
|
||||
/* reopen STREAMFILE from here, then pass it as an argument for our init function. */
|
||||
data->sf = reopen_streamfile(sf, 0);
|
||||
if (!data->sf) goto fail;
|
||||
data->handle = init_ongakukan_adpcm(data->sf, (long int)(data_offset), (long int)(data_size),
|
||||
sound_is_adpcm);
|
||||
if (!data->handle) goto fail;
|
||||
|
||||
return data;
|
||||
fail:
|
||||
free_ongakukan_adp(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void decode_ongakukan_adp(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do)
|
||||
{
|
||||
ongakukan_adp_data* data = vgmstream->codec_data;
|
||||
|
||||
data->getting_samples = 2;
|
||||
data->samples_done = 0;
|
||||
data->samples_filled = false;
|
||||
/* ^ samples_filled is boolean here because we need to simplify how decoding will work here.
|
||||
* so, rather than making samples_filled into a long int counter,
|
||||
* we make it into a boolean flag instead so as to let data->samples_done shine as a counter
|
||||
* and the decoder to do its job without worry. */
|
||||
|
||||
while (data->samples_done < samples_to_do)
|
||||
{
|
||||
if (data->samples_filled)
|
||||
{
|
||||
memcpy(outbuf + data->samples_done,
|
||||
data->samples,
|
||||
data->getting_samples * sizeof(int16_t));
|
||||
data->samples_done += data->getting_samples;
|
||||
|
||||
data->samples_filled = false;
|
||||
}
|
||||
else { decode_ongakukan_adpcm_data(data->handle);
|
||||
data->samples_filled = true;
|
||||
data->samples = (int16_t*)get_sample_hist_from_ongakukan_adpcm(data->handle); }
|
||||
}
|
||||
}
|
||||
|
||||
void reset_ongakukan_adp(ongakukan_adp_data* data)
|
||||
{
|
||||
if (!data) return;
|
||||
ongakukan_adpcm_reset(data->handle);
|
||||
}
|
||||
|
||||
void seek_ongakukan_adp(ongakukan_adp_data* data, int32_t current_sample)
|
||||
{
|
||||
if (!data) return;
|
||||
ongakukan_adpcm_seek(data->handle, current_sample);
|
||||
}
|
||||
|
||||
void free_ongakukan_adp(ongakukan_adp_data* data)
|
||||
{
|
||||
if (!data) return;
|
||||
close_streamfile(data->sf);
|
||||
ongakukan_adpcm_free(data->handle);
|
||||
free(data);
|
||||
}
|
||||
|
||||
int32_t ongakukan_adp_get_samples(ongakukan_adp_data* data)
|
||||
{
|
||||
if (!data) return 0;
|
||||
return (int32_t)get_num_samples_from_ongakukan_adpcm(data->handle);
|
||||
}
|
||||
#include <stdlib.h>
|
||||
#include "coding.h"
|
||||
#include "libs/ongakukan_adp_lib.h"
|
||||
|
||||
struct ongakukan_adp_data {
|
||||
void* handle;
|
||||
int16_t* samples;
|
||||
int32_t samples_done;
|
||||
bool samples_filled; /* false - no, true - yes */
|
||||
int32_t getting_samples; /* initialized to 2 on decode_ongakukan_adp. i mean, we literally get two decoded samples here. */
|
||||
STREAMFILE* sf;
|
||||
};
|
||||
|
||||
ongakukan_adp_data* init_ongakukan_adp(STREAMFILE* sf, int32_t data_offset, int32_t data_size, bool sound_is_adpcm) {
|
||||
ongakukan_adp_data* data = NULL;
|
||||
|
||||
data = calloc(1, sizeof(ongakukan_adp_data));
|
||||
if (!data) goto fail;
|
||||
|
||||
/* reopen STREAMFILE from here, then pass it as an argument for our init function. */
|
||||
data->sf = reopen_streamfile(sf, 0);
|
||||
if (!data->sf) goto fail;
|
||||
|
||||
data->handle = ongakukan_adpcm_init(data->sf, (long int)(data_offset), (long int)(data_size), sound_is_adpcm);
|
||||
if (!data->handle) goto fail;
|
||||
|
||||
return data;
|
||||
fail:
|
||||
free_ongakukan_adp(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void decode_ongakukan_adp(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do) {
|
||||
ongakukan_adp_data* data = vgmstream->codec_data;
|
||||
|
||||
data->getting_samples = 2;
|
||||
data->samples_done = 0;
|
||||
data->samples_filled = false;
|
||||
|
||||
while (data->samples_done < samples_to_do) {
|
||||
if (data->samples_filled) {
|
||||
memcpy(outbuf + data->samples_done, data->samples, data->getting_samples * sizeof(int16_t));
|
||||
data->samples_done += data->getting_samples;
|
||||
|
||||
data->samples_filled = false;
|
||||
}
|
||||
else {
|
||||
ongakukan_adpcm_decode_data(data->handle);
|
||||
|
||||
data->samples_filled = true;
|
||||
data->samples = ongakukan_adpcm_get_sample_hist(data->handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void reset_ongakukan_adp(ongakukan_adp_data* data) {
|
||||
if (!data) return;
|
||||
ongakukan_adpcm_reset(data->handle);
|
||||
}
|
||||
|
||||
void seek_ongakukan_adp(ongakukan_adp_data* data, int32_t current_sample) {
|
||||
if (!data) return;
|
||||
ongakukan_adpcm_seek(data->handle, current_sample);
|
||||
}
|
||||
|
||||
void free_ongakukan_adp(ongakukan_adp_data* data) {
|
||||
if (!data) return;
|
||||
close_streamfile(data->sf);
|
||||
ongakukan_adpcm_free(data->handle);
|
||||
free(data);
|
||||
}
|
||||
|
||||
int32_t ongakukan_adp_get_samples(ongakukan_adp_data* data) {
|
||||
if (!data) return 0;
|
||||
return (int32_t)ongakukan_adpcm_get_num_samples(data->handle);
|
||||
}
|
||||
|
@ -1053,7 +1053,7 @@ static const meta_info meta_info_list[] = {
|
||||
{meta_AIFF, "Apple AIFF header"},
|
||||
{meta_STR_SNDS, "3DO SNDS header"},
|
||||
{meta_WS_AUD, "Westwood Studios .AUD header"},
|
||||
{meta_PS2_IVB, "IVB/BVII header"},
|
||||
{meta_IIVB, "Vingt-et-un IIVB header"},
|
||||
{meta_SVS, "Square SVS header"},
|
||||
{meta_RIFF_WAVE, "RIFF WAVE header"},
|
||||
{meta_RIFF_WAVE_POS, "RIFF WAVE header (.pos looping)"},
|
||||
|
@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
@ -85,7 +85,6 @@
|
||||
<ClInclude Include="api_helpers.h" />
|
||||
<ClInclude Include="api_streamfile.h" />
|
||||
<ClInclude Include="api_tags.h" />
|
||||
<ClInclude Include="coding\libs\ongakukan_adp_lib.h" />
|
||||
<ClInclude Include="streamfile.h" />
|
||||
<ClInclude Include="streamtypes.h" />
|
||||
<ClInclude Include="util.h" />
|
||||
@ -116,6 +115,7 @@
|
||||
<ClInclude Include="coding\libs\icelib.h" />
|
||||
<ClInclude Include="coding\libs\libacm.h" />
|
||||
<ClInclude Include="coding\libs\nwa_lib.h" />
|
||||
<ClInclude Include="coding\libs\ongakukan_adp_lib.h" />
|
||||
<ClInclude Include="coding\libs\relic_lib.h" />
|
||||
<ClInclude Include="coding\libs\tac_data.h" />
|
||||
<ClInclude Include="coding\libs\tac_lib.h" />
|
||||
@ -207,10 +207,7 @@
|
||||
<ClInclude Include="util\zlib_vgmstream.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="coding\libs\ongakukan_adp_lib.c" />
|
||||
<ClCompile Include="coding\ongakukan_adp_decoder.c" />
|
||||
<ClCompile Include="formats.c" />
|
||||
<ClCompile Include="meta\adp_ongakukan.c" />
|
||||
<ClCompile Include="streamfile.c" />
|
||||
<ClCompile Include="util.c" />
|
||||
<ClCompile Include="vgmstream.c" />
|
||||
@ -283,6 +280,7 @@
|
||||
<ClCompile Include="coding\nwa_decoder.c" />
|
||||
<ClCompile Include="coding\ogg_vorbis_decoder.c" />
|
||||
<ClCompile Include="coding\oki_decoder.c" />
|
||||
<ClCompile Include="coding\ongakukan_adp_decoder.c" />
|
||||
<ClCompile Include="coding\pcm_decoder.c" />
|
||||
<ClCompile Include="coding\psv_decoder.c" />
|
||||
<ClCompile Include="coding\psx_decoder.c" />
|
||||
@ -318,6 +316,7 @@
|
||||
<ClCompile Include="coding\libs\libacm_decode.c" />
|
||||
<ClCompile Include="coding\libs\libacm_util.c" />
|
||||
<ClCompile Include="coding\libs\nwa_lib.c" />
|
||||
<ClCompile Include="coding\libs\ongakukan_adp_lib.c" />
|
||||
<ClCompile Include="coding\libs\relic_lib.c" />
|
||||
<ClCompile Include="coding\libs\relic_mixfft.c" />
|
||||
<ClCompile Include="coding\libs\tac_lib.c" />
|
||||
@ -379,6 +378,7 @@
|
||||
<ClCompile Include="meta\adm.c" />
|
||||
<ClCompile Include="meta\adpcm_capcom.c" />
|
||||
<ClCompile Include="meta\adp_konami.c" />
|
||||
<ClCompile Include="meta\adp_ongakukan.c" />
|
||||
<ClCompile Include="meta\adp_qd.c" />
|
||||
<ClCompile Include="meta\adp_wildfire.c" />
|
||||
<ClCompile Include="meta\ads.c" />
|
||||
@ -507,6 +507,7 @@
|
||||
<ClCompile Include="meta\idsp_ie.c" />
|
||||
<ClCompile Include="meta\idtech.c" />
|
||||
<ClCompile Include="meta\ifs.c" />
|
||||
<ClCompile Include="meta\iivb.c" />
|
||||
<ClCompile Include="meta\ikm.c" />
|
||||
<ClCompile Include="meta\ild.c" />
|
||||
<ClCompile Include="meta\ima.c" />
|
||||
@ -515,7 +516,6 @@
|
||||
<ClCompile Include="meta\isb.c" />
|
||||
<ClCompile Include="meta\ish_isd.c" />
|
||||
<ClCompile Include="meta\ivag.c" />
|
||||
<ClCompile Include="meta\ivb.c" />
|
||||
<ClCompile Include="meta\jstm.c" />
|
||||
<ClCompile Include="meta\kat.c" />
|
||||
<ClCompile Include="meta\kma9.c" />
|
||||
|
@ -179,6 +179,9 @@
|
||||
<ClInclude Include="coding\libs\nwa_lib.h">
|
||||
<Filter>coding\libs\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="coding\libs\ongakukan_adp_lib.h">
|
||||
<Filter>coding\libs\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="coding\libs\relic_lib.h">
|
||||
<Filter>coding\libs\Header Files</Filter>
|
||||
</ClInclude>
|
||||
@ -446,57 +449,6 @@
|
||||
<ClInclude Include="util\zlib_vgmstream.h">
|
||||
<Filter>util\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="coding\libs\circus_vq_data.h">
|
||||
<Filter>coding\libs\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="coding\libs\circus_vq_lib.h">
|
||||
<Filter>coding\libs\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="coding\libs\circus_vq_lzxpcm.h">
|
||||
<Filter>coding\libs\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="coding\libs\clhca.h">
|
||||
<Filter>coding\libs\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="coding\libs\compresswave_lib.h">
|
||||
<Filter>coding\libs\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="coding\libs\g7221_aes.h">
|
||||
<Filter>coding\libs\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="coding\libs\g7221_data.h">
|
||||
<Filter>coding\libs\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="coding\libs\g7221_lib.h">
|
||||
<Filter>coding\libs\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="coding\libs\icelib.h">
|
||||
<Filter>coding\libs\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="coding\libs\libacm.h">
|
||||
<Filter>coding\libs\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="coding\libs\nwa_lib.h">
|
||||
<Filter>coding\libs\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="coding\libs\ongakukan_adp_lib.h">
|
||||
<Filter>coding\libs\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="coding\libs\relic_lib.h">
|
||||
<Filter>coding\libs\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="coding\libs\tac_data.h">
|
||||
<Filter>coding\libs\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="coding\libs\tac_lib.h">
|
||||
<Filter>coding\libs\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="coding\libs\tac_ops.h">
|
||||
<Filter>coding\libs\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="coding\libs\utkdec.h">
|
||||
<Filter>coding\libs\Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="formats.c">
|
||||
@ -718,6 +670,9 @@
|
||||
<ClCompile Include="coding\oki_decoder.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\ongakukan_adp_decoder.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\pcm_decoder.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
@ -823,6 +778,9 @@
|
||||
<ClCompile Include="coding\libs\nwa_lib.c">
|
||||
<Filter>coding\libs\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\libs\ongakukan_adp_lib.c">
|
||||
<Filter>coding\libs\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\libs\relic_lib.c">
|
||||
<Filter>coding\libs\Source Files</Filter>
|
||||
</ClCompile>
|
||||
@ -1006,6 +964,9 @@
|
||||
<ClCompile Include="meta\adp_konami.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\adp_ongakukan.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\adp_qd.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
@ -1390,6 +1351,9 @@
|
||||
<ClCompile Include="meta\ifs.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\iivb.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\ikm.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
@ -1414,9 +1378,6 @@
|
||||
<ClCompile Include="meta\ivag.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\ivb.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\jstm.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
@ -2290,53 +2251,5 @@
|
||||
<ClCompile Include="util\text_reader.c">
|
||||
<Filter>util\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\adp_ongakukan.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\ongakukan_adp_decoder.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\libs\circus_vq_lib.c">
|
||||
<Filter>coding\libs\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\libs\clhca.c">
|
||||
<Filter>coding\libs\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\libs\compresswave_lib.c">
|
||||
<Filter>coding\libs\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\libs\g7221_aes.c">
|
||||
<Filter>coding\libs\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\libs\g7221_lib.c">
|
||||
<Filter>coding\libs\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\libs\icelib.c">
|
||||
<Filter>coding\libs\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\libs\libacm_decode.c">
|
||||
<Filter>coding\libs\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\libs\libacm_util.c">
|
||||
<Filter>coding\libs\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\libs\nwa_lib.c">
|
||||
<Filter>coding\libs\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\libs\ongakukan_adp_lib.c">
|
||||
<Filter>coding\libs\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\libs\relic_lib.c">
|
||||
<Filter>coding\libs\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\libs\relic_mixfft.c">
|
||||
<Filter>coding\libs\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\libs\tac_lib.c">
|
||||
<Filter>coding\libs\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\libs\utkdec.c">
|
||||
<Filter>coding\libs\Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -1,103 +1,84 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* Ongakukan RIFF with "ADP" extension [Train Simulator - Midousuji-sen (PS2)] */
|
||||
VGMSTREAM* init_vgmstream_ongakukan_adp(STREAMFILE* sf)
|
||||
{
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
size_t file_size;
|
||||
bool has_data_chunk = false, has_fact_chunk = false;
|
||||
int loop_flag = 0;
|
||||
int riff_wave_header_size = 0x2c;
|
||||
/* ^ where sound data begins, as a consequence their tools couldn't even write full RIFF WAVE header to file beyond that point.. */
|
||||
bool sound_is_adpcm = false;
|
||||
int32_t supposed_size, fmt_size, fmt_offset, offset_of_supposed_last_chunk;
|
||||
int32_t sample_rate, data_size;
|
||||
int16_t num_channels, block_size;
|
||||
|
||||
/* RIFF+WAVE checks */
|
||||
if (!is_id32be(0x00, sf, "RIFF")) goto fail;
|
||||
if (!is_id32be(0x08, sf, "WAVE")) goto fail;
|
||||
/* WAVE "fmt " check */
|
||||
if (!is_id32be(0x0c, sf, "fmt ")) goto fail;
|
||||
/* "adp" extension check (literally only one) */
|
||||
if (!check_extensions(sf, "adp")) goto fail;
|
||||
|
||||
/* catch adp file size from here and use it whenever needed. */
|
||||
file_size = get_streamfile_size(sf);
|
||||
|
||||
/* RIFF size from adp file can go beyond actual size (e.g: reported 10MB vs 2MB). do quick calcs around this. */
|
||||
supposed_size = ((read_s32le(0x04, sf) - 0x24) >> 2) + 0x2c;
|
||||
if (file_size != supposed_size) goto fail;
|
||||
|
||||
/* read entire WAVE "fmt " chunk. we start by reading fmt_size from yours truly and setting fmt_offset. */
|
||||
fmt_size = read_s32le(0x10, sf);
|
||||
fmt_offset = 0x14;
|
||||
if ((fmt_size >= 0x10) && (fmt_size <= 0x12)) /* depending on the adp, fmt_size alternates between 0x10 and 0x12 */
|
||||
{
|
||||
if (read_s16le(fmt_offset + 0, sf) != 1) goto fail; /* chunk reports codec number as signed little-endian PCM, couldn't be more wrong. */
|
||||
num_channels = read_s16le(fmt_offset + 2, sf);
|
||||
sample_rate = read_s32le(fmt_offset + 4, sf);
|
||||
if (read_s16le(fmt_offset + 14, sf) != 0x10) goto fail; /* bit depth as chunk reports it. */
|
||||
/* rest of fmt header is the usual header for 16-bit PCM wav files: bitrate, block size, and the like (see riff.c) */
|
||||
/* if fmt_size == 0x12 there is an additional s16 field that's always zero. */
|
||||
}
|
||||
else {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* now calc the var so we can read either "data" or "fact" chunk; */
|
||||
offset_of_supposed_last_chunk = fmt_offset + fmt_size;
|
||||
|
||||
/* we need to get to the last WAVE chunk manually, and that means the calc below. */
|
||||
offset_of_supposed_last_chunk = fmt_offset + fmt_size;
|
||||
if (is_id32be(offset_of_supposed_last_chunk + 0, sf, "data")) has_data_chunk = true;
|
||||
if (is_id32be(offset_of_supposed_last_chunk + 0, sf, "fact")) has_fact_chunk = true;
|
||||
|
||||
/* and because sound data *must* start at 0x2c, they have to bork both chunks too, so they're now essentially useless.
|
||||
* they're basically leftovers from original (lossless) WAV files at this point. */
|
||||
if (has_data_chunk)
|
||||
{
|
||||
/* RIFF adp files have leftover "data" chunk size... that does NOT match the ADP file size at hand. */
|
||||
supposed_size = (read_s32le(offset_of_supposed_last_chunk + 4, sf) >> 2) + 0x2c;
|
||||
if (file_size != supposed_size) goto fail;
|
||||
}
|
||||
|
||||
if (has_fact_chunk)
|
||||
{
|
||||
/* RIFF adp files have also cut off "fact" chunk so we're just left with a useless number now. */
|
||||
if (read_s16le(offset_of_supposed_last_chunk + 4, sf) != 4) goto fail;
|
||||
}
|
||||
|
||||
/* set start_offset value to riff_wave_header_size and calculate data_size by ourselves, basically how Ongakukan does it also. */
|
||||
start_offset = riff_wave_header_size;
|
||||
data_size = (int32_t)(file_size) - riff_wave_header_size;
|
||||
|
||||
/* Ongagukan games using this format just read it by checking "ADP" extension in an provided file name of a programmer's own choosing,
|
||||
* and if extension is there they just read the reported "number of samples" and "sample_rate" vars
|
||||
* from RIFF WAVE "fmt " chunk based on an already-opened file with that same name.
|
||||
* and they don't even read RIFF chunks, they just pick these two vars and that's basically it. */
|
||||
|
||||
/* our custom decoder needs at least one flag set. */
|
||||
sound_is_adpcm = true;
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(num_channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_ONGAKUKAN_RIFF_ADP;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->codec_data = init_ongakukan_adp(sf, start_offset, data_size, sound_is_adpcm);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_ONGAKUKAN_ADPCM;
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->num_samples = ongakukan_adp_get_samples(vgmstream->codec_data);
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* Ongakukan RIFF with "ADP" extension [Train Simulator: Midousuji-sen (PS2), Mobile Train Simulator (PSP)] */
|
||||
VGMSTREAM* init_vgmstream_adp_ongakukan(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag = 0;
|
||||
bool sound_is_adpcm = false;
|
||||
int32_t fmt_size, fmt_offset;
|
||||
int32_t sample_rate, data_size;
|
||||
int16_t channels;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!is_id32be(0x00, sf, "RIFF"))
|
||||
return NULL;
|
||||
if (!check_extensions(sf, "adp"))
|
||||
return NULL;
|
||||
|
||||
/* Format starts like RIFF but doesn't have valid chunks beyond fmt (ADPCM data overwrites anything after 0x2c). */
|
||||
|
||||
start_offset = 0x2c; /* fixed values, basically how Ongakukan does it */
|
||||
data_size = get_streamfile_size(sf) - start_offset;
|
||||
|
||||
/* RIFF size seem to match original PCM .wav, while encoded .adp data equals or is slightly smaller that that */
|
||||
uint32_t expected_size = (read_u32le(0x04, sf) - 0x24);
|
||||
uint32_t pcm_size = data_size * 2 * sizeof(short); // * channels
|
||||
if (pcm_size > expected_size)
|
||||
return NULL;
|
||||
|
||||
if (!is_id32be(0x08, sf, "WAVE"))
|
||||
return NULL;
|
||||
if (!is_id32be(0x0c, sf, "fmt "))
|
||||
return NULL;
|
||||
|
||||
fmt_size = read_s32le(0x10, sf);
|
||||
/* depending on the adp, fmt_size alternates between 0x10 and 0x12 */
|
||||
if (fmt_size < 0x10 || fmt_size > 0x12)
|
||||
goto fail;
|
||||
fmt_offset = 0x14;
|
||||
|
||||
if (read_s16le(fmt_offset + 0x00, sf) != 0x0001) /* PCM format */
|
||||
goto fail;
|
||||
channels = read_s16le(fmt_offset + 0x02, sf);
|
||||
if (channels != 1) /* not seen (decoder can't handle it) */
|
||||
goto fail;
|
||||
sample_rate = read_s32le(fmt_offset + 0x04, sf);
|
||||
if (read_s16le(fmt_offset + 0x0e, sf) != 16) /* PCM bit depth */
|
||||
goto fail;
|
||||
/* rest of fmt header is the usual header for 16-bit PCM wav files: bitrate, block size, and the like (see riff.c) */
|
||||
/* if fmt_size == 0x12 there may be is an additional s16 field that's always zero, but not always. */
|
||||
|
||||
/* next chunk is at fixed offset, regardless of fmt_size (fmt_size 0x12 with "data" at 0x24 is possible).
|
||||
* "data" has chunk size (does not match ADP size but original WAV) and "fact" chunk size 0x04 cut off) */
|
||||
if (!is_id32be(0x24, sf, "data") && !is_id32be(0x26, sf, "fact"))
|
||||
goto fail;
|
||||
|
||||
/* Ongagukan games using this format just read it by checking "ADP" extension in a provided file name,
|
||||
* and if extension is there they just read the reported "number of samples" and "sample_rate" vars
|
||||
* from RIFF WAVE "fmt " chunk based on an already-opened file with that same name.
|
||||
* and they don't even read RIFF chunks, they just pick these two vars and that's basically it. */
|
||||
|
||||
sound_is_adpcm = true;
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_ONGAKUKAN_RIFF_ADP;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->codec_data = init_ongakukan_adp(sf, start_offset, data_size, sound_is_adpcm);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_ONGAKUKAN_ADPCM;
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->num_samples = ongakukan_adp_get_samples(vgmstream->codec_data);
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
35
src/meta/iivb.c
Normal file
35
src/meta/iivb.c
Normal file
@ -0,0 +1,35 @@
|
||||
#include "meta.h"
|
||||
#include "../util.h"
|
||||
#include "../util/meta_utils.h"
|
||||
|
||||
|
||||
/* IIVB - from Vingt-et-un Systems games [Langrisser III (PS2), Ururun Quest: Koiyuuki (PS2)] */
|
||||
VGMSTREAM* init_vgmstream_iivb(STREAMFILE* sf) {
|
||||
|
||||
/* checks */
|
||||
if (!is_id32be(0x00, sf, "BVII")) /* IIVB LE, given extension */
|
||||
return NULL;
|
||||
if (!check_extensions(sf,"ivb"))
|
||||
return NULL;
|
||||
|
||||
|
||||
meta_header_t h = {
|
||||
.meta = meta_IIVB,
|
||||
};
|
||||
h.chan_size = read_u32le(0x04,sf);
|
||||
h.sample_rate = read_s32be(0x08,sf); /* big endian? */
|
||||
// 0c: empty
|
||||
|
||||
h.channels = 2;
|
||||
h.stream_offset = 0x10;
|
||||
h.num_samples = ps_bytes_to_samples(h.chan_size, 1);
|
||||
|
||||
h.coding = coding_PSX;
|
||||
h.layout = layout_interleave;
|
||||
h.interleave = h.chan_size;
|
||||
|
||||
h.sf = sf;
|
||||
h.open_stream = true;
|
||||
|
||||
return alloc_metastream(&h);
|
||||
}
|
@ -1,60 +0,0 @@
|
||||
#include "meta.h"
|
||||
#include "../util.h"
|
||||
|
||||
/* a simple PS2 ADPCM format seen in Langrisser 3 */
|
||||
VGMSTREAM * init_vgmstream_ivb(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
char filename[PATH_LIMIT];
|
||||
off_t start_offset;
|
||||
off_t stream_length;
|
||||
|
||||
int loop_flag = 0;
|
||||
int channel_count;
|
||||
|
||||
/* check extension, case insensitive */
|
||||
streamFile->get_name(streamFile,filename,sizeof(filename));
|
||||
if (strcasecmp("ivb",filename_extension(filename))) goto fail;
|
||||
|
||||
/* check header */
|
||||
if (read_32bitBE(0x00,streamFile) != 0x42564949) /* "BVII", probably */
|
||||
goto fail; /* supposed to be "IIVB"*/
|
||||
|
||||
loop_flag = 0;
|
||||
channel_count = 2;
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
/* fill in the vital statistics */
|
||||
vgmstream->channels = channel_count;
|
||||
vgmstream->sample_rate = read_32bitBE(0x8,streamFile); /* big endian? */
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
stream_length = read_32bitLE(0x04,streamFile);
|
||||
start_offset = 0x10;
|
||||
vgmstream->num_samples = stream_length*28/16;
|
||||
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->meta_type = meta_PS2_IVB;
|
||||
|
||||
/* open the file for reading */
|
||||
{
|
||||
int i;
|
||||
for (i=0;i<channel_count;i++) {
|
||||
vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
|
||||
|
||||
if (!vgmstream->ch[i].streamfile) goto fail;
|
||||
|
||||
vgmstream->ch[i].channel_start_offset=
|
||||
vgmstream->ch[i].offset=start_offset+stream_length*i;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return vgmstream;
|
||||
|
||||
/* clean up anything we may have opened */
|
||||
fail:
|
||||
if (vgmstream) close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
@ -182,7 +182,7 @@ VGMSTREAM * init_vgmstream_ws_aud(STREAMFILE * streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_ahx(STREAMFILE * streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_ivb(STREAMFILE * streamFile);
|
||||
VGMSTREAM* init_vgmstream_iivb(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM * init_vgmstream_svs(STREAMFILE * streamFile);
|
||||
|
||||
@ -1018,6 +1018,6 @@ VGMSTREAM* init_vgmstream_ea_sbk(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM* init_vgmstream_dsp_asura_sfx(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM* init_vgmstream_ongakukan_adp(STREAMFILE* sf);
|
||||
VGMSTREAM* init_vgmstream_adp_ongakukan(STREAMFILE* sf);
|
||||
|
||||
#endif /*_META_H*/
|
||||
|
@ -5,10 +5,14 @@
|
||||
/* Allocate memory and setup a VGMSTREAM */
|
||||
VGMSTREAM* alloc_metastream(meta_header_t* h) {
|
||||
|
||||
if (h->sample_rate <= 0 || h->sample_rate > VGMSTREAM_MAX_SAMPLE_RATE)
|
||||
if (h->sample_rate <= 0 || h->sample_rate > VGMSTREAM_MAX_SAMPLE_RATE) {
|
||||
VGM_LOG("meta: wrong sample rate %i\n", h->sample_rate);
|
||||
return NULL;
|
||||
if (h->num_samples <= 0 || h->num_samples > VGMSTREAM_MAX_NUM_SAMPLES)
|
||||
}
|
||||
if (h->num_samples <= 0 || h->num_samples > VGMSTREAM_MAX_NUM_SAMPLES) {
|
||||
VGM_LOG("meta: wrong samples %i\n", h->sample_rate);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
VGMSTREAM* vgmstream = allocate_vgmstream(h->channels, h->loop_flag);
|
||||
if (!vgmstream) return NULL;
|
||||
|
@ -66,7 +66,7 @@ init_vgmstream_t init_vgmstream_functions[] = {
|
||||
init_vgmstream_str_snds,
|
||||
init_vgmstream_ws_aud,
|
||||
init_vgmstream_ahx,
|
||||
init_vgmstream_ivb,
|
||||
init_vgmstream_iivb,
|
||||
init_vgmstream_svs,
|
||||
init_vgmstream_riff,
|
||||
init_vgmstream_rifx,
|
||||
@ -533,7 +533,7 @@ init_vgmstream_t init_vgmstream_functions[] = {
|
||||
init_vgmstream_dsp_asura_ds2,
|
||||
init_vgmstream_dsp_asura_ttss,
|
||||
init_vgmstream_dsp_asura_sfx,
|
||||
init_vgmstream_ongakukan_adp,
|
||||
init_vgmstream_adp_ongakukan,
|
||||
|
||||
/* lower priority metas (no clean header identity, somewhat ambiguous, or need extension/companion file to identify) */
|
||||
init_vgmstream_agsc,
|
||||
|
@ -318,7 +318,7 @@ typedef enum {
|
||||
meta_PWB,
|
||||
meta_VPK, /* VPK Audio File */
|
||||
meta_PS2_BMDX, /* Beatmania thing */
|
||||
meta_PS2_IVB, /* Langrisser 3 IVB */
|
||||
meta_IIVB,
|
||||
meta_PS2_SND, /* some Might & Magics SSND header */
|
||||
meta_SVS, /* Square SVS */
|
||||
meta_XSS, /* Dino Crisis 3 */
|
||||
|
8
vspf.py
8
vspf.py
@ -150,7 +150,13 @@ class ProjectFixer:
|
||||
def main():
|
||||
types = [
|
||||
'./src/*.vcxproj',
|
||||
'./src/*.vcxproj.filters'
|
||||
'./src/*.vcxproj.filters',
|
||||
#'./cli/*.vcxproj',
|
||||
#'./cli/*.vcxproj.filters',
|
||||
'./winamp/*.vcxproj',
|
||||
'./winamp/*.vcxproj.filters',
|
||||
'./xmplay/*.vcxproj',
|
||||
'./xmplay/*.vcxproj.filters',
|
||||
]
|
||||
|
||||
for type in types:
|
||||
|
@ -33,14 +33,14 @@ TARGET_EXT_LIBS += $(LIBS_TARGET_EXT_LIBS)
|
||||
|
||||
export CFLAGS LDFLAGS
|
||||
|
||||
#SRC_SRCS = $(wildcard *.c)
|
||||
SRC_SRCS = in_vgmstream.c in_streamfile.c in_config.c
|
||||
SRCS = $(wildcard *.c)
|
||||
OBJECTS = $(wildcard *.o)
|
||||
|
||||
###############################################################################
|
||||
### targets
|
||||
|
||||
in_vgmstream: libvgmstream.a $(TARGET_EXT_LIBS) resource.o
|
||||
$(CC) -shared -static-libgcc $(CFLAGS) $(SRC_SRCS) resource.o $(LDFLAGS) -o $(OUTPUT_WINAMP)
|
||||
$(CC) -shared -static-libgcc $(CFLAGS) $(SRCS) resource.o $(LDFLAGS) -o $(OUTPUT_WINAMP)
|
||||
$(STRIP) $(OUTPUT_WINAMP)
|
||||
|
||||
resource.o: resource.rc resource.h
|
||||
@ -53,6 +53,6 @@ $(TARGET_EXT_LIBS):
|
||||
$(MAKE) -C ../ext_libs $@
|
||||
|
||||
clean:
|
||||
$(RMF) $(OUTPUT_WINAMP) resource.o
|
||||
$(RMF) $(OUTPUT_WINAMP) $(OBJECTS) resource.o
|
||||
|
||||
.PHONY: clean in_vgmstream libvgmstream.a $(TARGET_EXT_LIBS)
|
||||
|
@ -9,8 +9,8 @@
|
||||
/* ************************************* */
|
||||
|
||||
/* config */
|
||||
#define CONFIG_APP_NAME TEXT("vgmstream plugin")
|
||||
#define CONFIG_INI_NAME TEXT("plugin.ini")
|
||||
#define CONFIG_APP_NAME TEXT("vgmstream plugin")
|
||||
#define CONFIG_INI_NAME TEXT("plugin.ini")
|
||||
|
||||
#define INI_FADE_TIME TEXT("fade_seconds")
|
||||
#define INI_FADE_DELAY TEXT("fade_delay")
|
||||
@ -28,28 +28,28 @@
|
||||
#define INI_CLIP_TYPE TEXT("clip_type")
|
||||
|
||||
TCHAR *dlg_priority_strings[7] = {
|
||||
TEXT("Idle"),
|
||||
TEXT("Lowest"),
|
||||
TEXT("Below Normal"),
|
||||
TEXT("Normal"),
|
||||
TEXT("Above Normal"),
|
||||
TEXT("Highest (not recommended)"),
|
||||
TEXT("Time Critical (not recommended)")
|
||||
TEXT("Idle"),
|
||||
TEXT("Lowest"),
|
||||
TEXT("Below Normal"),
|
||||
TEXT("Normal"),
|
||||
TEXT("Above Normal"),
|
||||
TEXT("Highest (not recommended)"),
|
||||
TEXT("Time Critical (not recommended)")
|
||||
};
|
||||
TCHAR *dlg_replaygain_strings[] = {
|
||||
TEXT("None"),
|
||||
TEXT("Album"),
|
||||
TEXT("Peak")
|
||||
TEXT("None"),
|
||||
TEXT("Album"),
|
||||
TEXT("Peak")
|
||||
};
|
||||
|
||||
int priority_values[7] = {
|
||||
THREAD_PRIORITY_IDLE,
|
||||
THREAD_PRIORITY_LOWEST,
|
||||
THREAD_PRIORITY_BELOW_NORMAL,
|
||||
THREAD_PRIORITY_NORMAL,
|
||||
THREAD_PRIORITY_ABOVE_NORMAL,
|
||||
THREAD_PRIORITY_HIGHEST,
|
||||
THREAD_PRIORITY_TIME_CRITICAL
|
||||
const int priority_values[7] = {
|
||||
THREAD_PRIORITY_IDLE,
|
||||
THREAD_PRIORITY_LOWEST,
|
||||
THREAD_PRIORITY_BELOW_NORMAL,
|
||||
THREAD_PRIORITY_NORMAL,
|
||||
THREAD_PRIORITY_ABOVE_NORMAL,
|
||||
THREAD_PRIORITY_HIGHEST,
|
||||
THREAD_PRIORITY_TIME_CRITICAL
|
||||
};
|
||||
|
||||
|
||||
|
120
winamp/in_utils.c
Normal file
120
winamp/in_utils.c
Normal file
@ -0,0 +1,120 @@
|
||||
#include "in_vgmstream.h"
|
||||
|
||||
|
||||
/* Adds ext to Winamp's extension list */
|
||||
static int add_extension(char* dst, int dst_len, const char* ext) {
|
||||
int ext_len;
|
||||
|
||||
ext_len = strlen(ext);
|
||||
if (dst_len <= ext_len + 1)
|
||||
return 0;
|
||||
|
||||
strcpy(dst, ext); /* seems winamp uppercases this if needed */
|
||||
dst[ext_len] = ';';
|
||||
|
||||
return ext_len + 1;
|
||||
}
|
||||
|
||||
/* Creates Winamp's extension list, a single string that ends with \0\0.
|
||||
* Each extension must be in this format: "extensions\0Description\0"
|
||||
*
|
||||
* The list is used to accept extensions by default when IsOurFile returns 0, to register file
|
||||
* types, and in the open dialog's type combo. Format actually can be:
|
||||
* - "ext1;ext2;...\0EXTS Audio Files (*.ext1; *.ext2; *...\0", //single line with all
|
||||
* - "ext1\0EXT1 Audio File (*.ext1)\0ext2\0EXT2 Audio File (*.ext2)\0...", //multiple lines
|
||||
* Open dialog's text (including all plugin's "Description") seems limited to old MAX_PATH 260
|
||||
* (max size for "extensions" checks seems ~0x40000 though). Given vgmstream's huge number
|
||||
* of exts, use single line to (probably) work properly with dialogs (used to be multi line).
|
||||
*/
|
||||
void build_extension_list(char* winamp_list, int winamp_list_size) {
|
||||
const char** ext_list;
|
||||
size_t ext_list_len;
|
||||
int i, written;
|
||||
int description_size = 0x100; /* reserved max at the end */
|
||||
|
||||
winamp_list[0] = '\0';
|
||||
winamp_list[1] = '\0';
|
||||
|
||||
ext_list = vgmstream_get_formats(&ext_list_len);
|
||||
|
||||
for (i = 0; i < ext_list_len; i++) {
|
||||
int used = add_extension(winamp_list, winamp_list_size - description_size, ext_list[i]);
|
||||
if (used <= 0) {
|
||||
vgm_logi("build_extension_list: not enough buf for all exts\n");
|
||||
break;
|
||||
}
|
||||
winamp_list += used;
|
||||
winamp_list_size -= used;
|
||||
}
|
||||
if (i > 0) {
|
||||
winamp_list[-1] = '\0'; /* last "ext;" to "ext\0" */
|
||||
}
|
||||
|
||||
/* generic description for the info dialog since we can't really show everything */
|
||||
written = snprintf(winamp_list, winamp_list_size - 2, "vgmstream Audio Files%c", '\0');
|
||||
|
||||
/* should end with double \0 */
|
||||
if (written < 0) {
|
||||
winamp_list[0] = '\0';
|
||||
winamp_list[1] = '\0';
|
||||
}
|
||||
else {
|
||||
winamp_list[written + 0] = '\0';
|
||||
winamp_list[written + 1] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* makes a modified filename, suitable to pass parameters around */
|
||||
static void make_fn_subsong(in_char* dst, int dst_size, const in_char* filename, int subsong_index) {
|
||||
/* Follows "(file)(config)(ext)". Winamp needs to "see" (ext) to validate, and file goes first so relative
|
||||
* files work in M3Us (path is added). Protocols a la "vgmstream://(config)(file)" work but don't get full paths. */
|
||||
wa_snprintf(dst,dst_size, wa_L("%s|$s=%i|.vgmstream"), filename, subsong_index);
|
||||
}
|
||||
|
||||
/* unpacks the subsongs by adding entries to the playlist */
|
||||
bool split_subsongs(const in_char* filename, int subsong_index, VGMSTREAM* vgmstream) {
|
||||
int i, playlist_index;
|
||||
HWND hPlaylistWindow;
|
||||
|
||||
|
||||
if (settings.disable_subsongs || vgmstream->num_streams <= 1)
|
||||
return 0; /* don't split if no subsongs */
|
||||
if (subsong_index > 0 || vgmstream->stream_index > 0)
|
||||
return 0; /* no split if already playing subsong */
|
||||
|
||||
hPlaylistWindow = (HWND)SendMessage(input_module.hMainWindow, WM_WA_IPC, IPC_GETWND_PE, IPC_GETWND);
|
||||
playlist_index = SendMessage(input_module.hMainWindow,WM_WA_IPC,0,IPC_GETLISTPOS);
|
||||
|
||||
/* The only way to pass info around in Winamp is encoding it into the filename, so a fake name
|
||||
* is created with the index. Then, winamp_Play (and related) intercepts and reads the index. */
|
||||
for (i = 0; i < vgmstream->num_streams; i++) {
|
||||
in_char stream_fn[PATH_LIMIT];
|
||||
|
||||
make_fn_subsong(stream_fn,PATH_LIMIT, filename, (i+1)); /* encode index in filename */
|
||||
|
||||
/* insert at index */
|
||||
{
|
||||
COPYDATASTRUCT cds = {0};
|
||||
wa_fileinfo f;
|
||||
|
||||
wa_strncpy(f.file, stream_fn,MAX_PATH-1);
|
||||
f.file[MAX_PATH-1] = '\0';
|
||||
f.index = playlist_index + (i+1);
|
||||
cds.dwData = wa_IPC_PE_INSERTFILENAME;
|
||||
cds.lpData = (void*)&f;
|
||||
cds.cbData = sizeof(wa_fileinfo);
|
||||
SendMessage(hPlaylistWindow,WM_COPYDATA,0,(LPARAM)&cds);
|
||||
}
|
||||
/* IPC_ENQUEUEFILE can pre-set the title without needing the Playlist handle, but can't insert at index */
|
||||
}
|
||||
|
||||
/* remove current file from the playlist */
|
||||
SendMessage(hPlaylistWindow, WM_WA_IPC, IPC_PE_DELETEINDEX, playlist_index);
|
||||
|
||||
/* autoplay doesn't always advance to the first unpacked track, but manually fails somehow */
|
||||
//SendMessage(input_module.hMainWindow,WM_WA_IPC,playlist_index,IPC_SETPLAYLISTPOS);
|
||||
//SendMessage(input_module.hMainWindow,WM_WA_IPC,0,IPC_STARTPLAY);
|
||||
|
||||
return 1;
|
||||
}
|
@ -27,7 +27,7 @@ DWORD WINAPI __stdcall decode(void *arg);
|
||||
* (older versions of XMPlay also will crash with so many exts but can't autodetect version here) */
|
||||
#define EXTENSION_LIST_SIZE (0x2000 * 6)
|
||||
/* fixed list to simplify but could also malloc/free on init/close */
|
||||
char working_extension_list[EXTENSION_LIST_SIZE] = {0};
|
||||
char extension_list[EXTENSION_LIST_SIZE] = {0};
|
||||
|
||||
|
||||
/* current play state */
|
||||
@ -127,61 +127,6 @@ static VGMSTREAM* init_vgmstream_winamp_fileinfo(const in_char* fn) {
|
||||
}
|
||||
|
||||
|
||||
/* makes a modified filename, suitable to pass parameters around */
|
||||
static void make_fn_subsong(in_char* dst, int dst_size, const in_char* filename, int stream_index) {
|
||||
/* Follows "(file)(config)(ext)". Winamp needs to "see" (ext) to validate, and file goes first so relative
|
||||
* files work in M3Us (path is added). Protocols a la "vgmstream://(config)(file)" work but don't get full paths. */
|
||||
wa_snprintf(dst,dst_size, wa_L("%s|$s=%i|.vgmstream"), filename, stream_index);
|
||||
}
|
||||
|
||||
/* unpacks the subsongs by adding entries to the playlist */
|
||||
static int split_subsongs(const in_char* filename, int stream_index, VGMSTREAM *vgmstream) {
|
||||
int i, playlist_index;
|
||||
HWND hPlaylistWindow;
|
||||
|
||||
|
||||
if (settings.disable_subsongs || vgmstream->num_streams <= 1)
|
||||
return 0; /* don't split if no subsongs */
|
||||
if (stream_index > 0 || vgmstream->stream_index > 0)
|
||||
return 0; /* no split if already playing subsong */
|
||||
|
||||
hPlaylistWindow = (HWND)SendMessage(input_module.hMainWindow, WM_WA_IPC, IPC_GETWND_PE, IPC_GETWND);
|
||||
playlist_index = SendMessage(input_module.hMainWindow,WM_WA_IPC,0,IPC_GETLISTPOS);
|
||||
|
||||
/* The only way to pass info around in Winamp is encoding it into the filename, so a fake name
|
||||
* is created with the index. Then, winamp_Play (and related) intercepts and reads the index. */
|
||||
for (i = 0; i < vgmstream->num_streams; i++) {
|
||||
in_char stream_fn[PATH_LIMIT];
|
||||
|
||||
make_fn_subsong(stream_fn,PATH_LIMIT, filename, (i+1)); /* encode index in filename */
|
||||
|
||||
/* insert at index */
|
||||
{
|
||||
COPYDATASTRUCT cds = {0};
|
||||
wa_fileinfo f;
|
||||
|
||||
wa_strncpy(f.file, stream_fn,MAX_PATH-1);
|
||||
f.file[MAX_PATH-1] = '\0';
|
||||
f.index = playlist_index + (i+1);
|
||||
cds.dwData = wa_IPC_PE_INSERTFILENAME;
|
||||
cds.lpData = (void*)&f;
|
||||
cds.cbData = sizeof(wa_fileinfo);
|
||||
SendMessage(hPlaylistWindow,WM_COPYDATA,0,(LPARAM)&cds);
|
||||
}
|
||||
/* IPC_ENQUEUEFILE can pre-set the title without needing the Playlist handle, but can't insert at index */
|
||||
}
|
||||
|
||||
/* remove current file from the playlist */
|
||||
SendMessage(hPlaylistWindow, WM_WA_IPC, IPC_PE_DELETEINDEX, playlist_index);
|
||||
|
||||
/* autoplay doesn't always advance to the first unpacked track, but manually fails somehow */
|
||||
//SendMessage(input_module.hMainWindow,WM_WA_IPC,playlist_index,IPC_SETPLAYLISTPOS);
|
||||
//SendMessage(input_module.hMainWindow,WM_WA_IPC,0,IPC_STARTPLAY);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* try to detect XMPlay, which can't interact with the playlist = no splitting */
|
||||
static int is_xmplay() {
|
||||
if (GetModuleHandle( TEXT("xmplay.exe") ))
|
||||
@ -194,70 +139,6 @@ static int is_xmplay() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Adds ext to Winamp's extension list */
|
||||
static int add_extension(char* dst, int dst_len, const char* ext) {
|
||||
int ext_len;
|
||||
|
||||
ext_len = strlen(ext);
|
||||
if (dst_len <= ext_len + 1)
|
||||
return 0;
|
||||
|
||||
strcpy(dst, ext); /* seems winamp uppercases this if needed */
|
||||
dst[ext_len] = ';';
|
||||
|
||||
return ext_len + 1;
|
||||
}
|
||||
|
||||
/* Creates Winamp's extension list, a single string that ends with \0\0.
|
||||
* Each extension must be in this format: "extensions\0Description\0"
|
||||
*
|
||||
* The list is used to accept extensions by default when IsOurFile returns 0, to register file
|
||||
* types, and in the open dialog's type combo. Format actually can be:
|
||||
* - "ext1;ext2;...\0EXTS Audio Files (*.ext1; *.ext2; *...\0", //single line with all
|
||||
* - "ext1\0EXT1 Audio File (*.ext1)\0ext2\0EXT2 Audio File (*.ext2)\0...", //multiple lines
|
||||
* Open dialog's text (including all plugin's "Description") seems limited to old MAX_PATH 260
|
||||
* (max size for "extensions" checks seems ~0x40000 though). Given vgmstream's huge number
|
||||
* of exts, use single line to (probably) work properly with dialogs (used to be multi line).
|
||||
*/
|
||||
static void build_extension_list(char* winamp_list, int winamp_list_size) {
|
||||
const char** ext_list;
|
||||
size_t ext_list_len;
|
||||
int i, written;
|
||||
int description_size = 0x100; /* reserved max at the end */
|
||||
|
||||
winamp_list[0] = '\0';
|
||||
winamp_list[1] = '\0';
|
||||
|
||||
ext_list = vgmstream_get_formats(&ext_list_len);
|
||||
|
||||
for (i = 0; i < ext_list_len; i++) {
|
||||
int used = add_extension(winamp_list, winamp_list_size - description_size, ext_list[i]);
|
||||
if (used <= 0) {
|
||||
vgm_logi("build_extension_list: not enough buf for all exts\n");
|
||||
break;
|
||||
}
|
||||
winamp_list += used;
|
||||
winamp_list_size -= used;
|
||||
}
|
||||
if (i > 0) {
|
||||
winamp_list[-1] = '\0'; /* last "ext;" to "ext\0" */
|
||||
}
|
||||
|
||||
/* generic description for the info dialog since we can't really show everything */
|
||||
written = snprintf(winamp_list, winamp_list_size - 2, "vgmstream Audio Files%c", '\0');
|
||||
|
||||
/* should end with double \0 */
|
||||
if (written < 0) {
|
||||
winamp_list[0] = '\0';
|
||||
winamp_list[1] = '\0';
|
||||
}
|
||||
else {
|
||||
winamp_list[written + 0] = '\0';
|
||||
winamp_list[written + 1] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
/* unicode utils */
|
||||
static void get_title(in_char* dst, int dst_size, const in_char* fn, VGMSTREAM* infostream) {
|
||||
in_char filename[PATH_LIMIT];
|
||||
@ -388,7 +269,7 @@ void winamp_Init() {
|
||||
}
|
||||
|
||||
/* dynamically make a list of supported extensions */
|
||||
build_extension_list(working_extension_list, sizeof(working_extension_list));
|
||||
build_extension_list(extension_list, sizeof(extension_list));
|
||||
}
|
||||
|
||||
/* called at program quit */
|
||||
@ -872,7 +753,7 @@ In_Module input_module = {
|
||||
PLUGIN_NAME,
|
||||
0, /* hMainWindow (filled in by Winamp) */
|
||||
0, /* hDllInstance (filled in by Winamp) */
|
||||
working_extension_list,
|
||||
extension_list,
|
||||
1, /* is_seekable flag */
|
||||
9, /* UsesOutputPlug flag */
|
||||
winamp_Config,
|
||||
|
@ -34,7 +34,7 @@
|
||||
/* ************************************* */
|
||||
|
||||
extern In_Module input_module;
|
||||
extern int priority_values[7];
|
||||
extern const int priority_values[7];
|
||||
|
||||
typedef enum {
|
||||
REPLAYGAIN_NONE,
|
||||
@ -87,8 +87,8 @@ extern winamp_log_t* walog;
|
||||
/* ************************************* */
|
||||
/* IN_UNICODE */
|
||||
/* ************************************* */
|
||||
//todo safe ops
|
||||
//todo there must be a better way to handle unicode...
|
||||
//TODO safe ops
|
||||
//TODO there must be a better way to handle unicode...
|
||||
#ifdef UNICODE_INPUT_PLUGIN
|
||||
#define wa_strcmp wcscmp
|
||||
#define wa_strncmp wcsncmp
|
||||
@ -190,11 +190,11 @@ static inline void cfg_char_to_wchar(TCHAR *wdst, size_t wdstsize, const char *s
|
||||
}
|
||||
|
||||
|
||||
/* ************************************* */
|
||||
/* IN_STREAMFILE */
|
||||
/* ************************************* */
|
||||
|
||||
/* in_streamfile.c */
|
||||
STREAMFILE* open_winamp_streamfile_by_ipath(const in_char* wpath);
|
||||
|
||||
#endif /*_IN_VGMSTREAM_*/
|
||||
void build_extension_list(char* extension_list, int list_size);
|
||||
|
||||
bool split_subsongs(const in_char* filename, int subsong_index, VGMSTREAM* vgmstream);
|
||||
|
||||
#endif
|
||||
|
@ -67,10 +67,15 @@
|
||||
<ItemGroup>
|
||||
<ClInclude Include="in_vgmstream.h" />
|
||||
<ClInclude Include="resource.h" />
|
||||
<ClInclude Include="sdk\in2.h" />
|
||||
<ClInclude Include="sdk\ipc_pe.h" />
|
||||
<ClInclude Include="sdk\out.h" />
|
||||
<ClInclude Include="sdk\wa_ipc.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="in_config.c" />
|
||||
<ClCompile Include="in_streamfile.c" />
|
||||
<ClCompile Include="in_utils.c" />
|
||||
<ClCompile Include="in_vgmstream.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
@ -21,6 +21,18 @@
|
||||
<ClInclude Include="resource.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="sdk\in2.h">
|
||||
<Filter>sdk\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="sdk\ipc_pe.h">
|
||||
<Filter>sdk\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="sdk\out.h">
|
||||
<Filter>sdk\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="sdk\wa_ipc.h">
|
||||
<Filter>sdk\Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="resource.rc">
|
||||
@ -34,6 +46,9 @@
|
||||
<ClCompile Include="in_streamfile.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="in_utils.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="in_vgmstream.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
@ -31,12 +31,14 @@ TARGET_EXT_LIBS += $(LIBS_TARGET_EXT_LIBS)
|
||||
|
||||
export CFLAGS LDFLAGS
|
||||
|
||||
SRCS = $(wildcard *.c)
|
||||
OBJECTS = $(wildcard *.o)
|
||||
|
||||
###############################################################################
|
||||
### targets
|
||||
|
||||
xmp_vgmstream: libvgmstream.a $(TARGET_EXT_LIBS)
|
||||
$(CC) -shared -static-libgcc $(CFLAGS) xmp_vgmstream.c $(LDFLAGS) -o $(OUTPUT_XMPLAY) xmpin.def
|
||||
$(CC) -shared -static-libgcc $(CFLAGS) $(SRCS) $(LDFLAGS) -o $(OUTPUT_XMPLAY) xmpin.def
|
||||
$(STRIP) $(OUTPUT_XMPLAY)
|
||||
|
||||
libvgmstream.a:
|
||||
@ -46,6 +48,6 @@ $(TARGET_EXT_LIBS):
|
||||
$(MAKE) -C ../ext_libs $@
|
||||
|
||||
clean:
|
||||
$(RMF) $(OUTPUT_XMPLAY)
|
||||
$(RMF) $(OBJECTS) $(OUTPUT_XMPLAY)
|
||||
|
||||
.PHONY: clean xmp_vgmstream libvgmstream.a $(TARGET_EXT_LIBS)
|
||||
|
@ -69,8 +69,11 @@
|
||||
<ItemGroup>
|
||||
<ClInclude Include="xmpfunc.h" />
|
||||
<ClInclude Include="xmpin.h" />
|
||||
<ClInclude Include="xmp_vgmstream.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="xmp_streamfile.c" />
|
||||
<ClCompile Include="xmp_utils.c" />
|
||||
<ClCompile Include="xmp_vgmstream.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
@ -1,11 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<ClCompile Include="xmp_vgmstream.c" />
|
||||
<ClCompile Include="xmp_streamfile.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="xmp_utils.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="xmp_vgmstream.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="xmpfunc.h" />
|
||||
<ClInclude Include="xmpin.h" />
|
||||
<ClInclude Include="xmpfunc.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="xmpin.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="xmp_vgmstream.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="xmpin.def" />
|
||||
|
86
xmplay/xmp_streamfile.c
Normal file
86
xmplay/xmp_streamfile.c
Normal file
@ -0,0 +1,86 @@
|
||||
#include "xmp_vgmstream.h"
|
||||
|
||||
/* a STREAMFILE that operates via XMPlay's XMPFUNC_FILE+XMPFILE */
|
||||
typedef struct _XMPLAY_STREAMFILE {
|
||||
STREAMFILE sf; /* callbacks */
|
||||
XMPFILE infile; /* actual FILE */
|
||||
XMPFUNC_FILE* xmpf_file; /* helper */
|
||||
char name[0x8000]; /* path limit */
|
||||
off_t offset; /* current offset */
|
||||
int internal_xmpfile; /* infile was not supplied externally and can be closed */
|
||||
} XMPLAY_STREAMFILE;
|
||||
|
||||
|
||||
static size_t xmpsf_read(XMPLAY_STREAMFILE* sf, uint8_t* dst, offv_t offset, size_t length) {
|
||||
size_t read;
|
||||
|
||||
if (sf->offset != offset) {
|
||||
if (sf->xmpf_file->Seek(sf->infile, offset))
|
||||
sf->offset = offset;
|
||||
else
|
||||
sf->offset = sf->xmpf_file->Tell(sf->infile);
|
||||
}
|
||||
|
||||
read = sf->xmpf_file->Read(sf->infile, dst, length);
|
||||
if (read > 0)
|
||||
sf->offset += read;
|
||||
|
||||
return read;
|
||||
}
|
||||
|
||||
static off_t xmpsf_get_size(XMPLAY_STREAMFILE* sf) {
|
||||
return sf->xmpf_file->GetSize(sf->infile);
|
||||
}
|
||||
|
||||
static off_t xmpsf_get_offset(XMPLAY_STREAMFILE* sf) {
|
||||
return sf->xmpf_file->Tell(sf->infile);
|
||||
}
|
||||
|
||||
static void xmpsf_get_name(XMPLAY_STREAMFILE* sf, char* buffer, size_t length) {
|
||||
snprintf(buffer, length, "%s", sf->name);
|
||||
buffer[length - 1] = '\0';
|
||||
}
|
||||
|
||||
static STREAMFILE* xmpsf_open(XMPLAY_STREAMFILE* sf, const char* const filename, size_t buffersize) {
|
||||
XMPFILE newfile;
|
||||
|
||||
if (!filename)
|
||||
return NULL;
|
||||
|
||||
newfile = sf->xmpf_file->Open(filename);
|
||||
if (!newfile) return NULL;
|
||||
|
||||
return open_xmplay_streamfile_by_xmpfile(newfile, sf->xmpf_file, filename, true); /* internal XMPFILE */
|
||||
}
|
||||
|
||||
static void xmpsf_close(XMPLAY_STREAMFILE* sf) {
|
||||
/* Close XMPFILE, but only if we opened it (ex. for subfiles inside metas).
|
||||
* Otherwise must be left open as other parts of XMPlay need it and would crash. */
|
||||
if (sf->internal_xmpfile) {
|
||||
sf->xmpf_file->Close(sf->infile);
|
||||
}
|
||||
|
||||
free(sf);
|
||||
}
|
||||
|
||||
STREAMFILE* open_xmplay_streamfile_by_xmpfile(XMPFILE infile, XMPFUNC_FILE* xmpf_file, const char* path, bool internal) {
|
||||
XMPLAY_STREAMFILE* this_sf = calloc(1, sizeof(XMPLAY_STREAMFILE));
|
||||
if (!this_sf) return NULL;
|
||||
|
||||
this_sf->sf.read = (void*)xmpsf_read;
|
||||
this_sf->sf.get_size = (void*)xmpsf_get_size;
|
||||
this_sf->sf.get_offset = (void*)xmpsf_get_offset;
|
||||
this_sf->sf.get_name = (void*)xmpsf_get_name;
|
||||
this_sf->sf.open = (void*)xmpsf_open;
|
||||
this_sf->sf.close = (void*)xmpsf_close;
|
||||
this_sf->infile = infile;
|
||||
this_sf->offset = 0;
|
||||
|
||||
snprintf(this_sf->name, sizeof(this_sf->name), "%s", path);
|
||||
this_sf->name[sizeof(this_sf->name) - 1] = '\0';
|
||||
|
||||
this_sf->internal_xmpfile = internal;
|
||||
this_sf->xmpf_file = xmpf_file;
|
||||
|
||||
return &this_sf->sf; /* pointer to STREAMFILE start = rest of the custom data follows */
|
||||
}
|
262
xmplay/xmp_utils.c
Normal file
262
xmplay/xmp_utils.c
Normal file
@ -0,0 +1,262 @@
|
||||
#include "xmp_vgmstream.h"
|
||||
|
||||
/* with <v3.8.5.62, any more than ~1000 will crash XMplay's file list screen. */
|
||||
#define EXTENSION_LIST_SIZE_OLD 1000
|
||||
#define EXTENSION_LIST_SIZE_OLD_VERSION 0x0308053d /* less than v3.8.5.62 */
|
||||
|
||||
|
||||
/* Adds ext to XMPlay's extension list. */
|
||||
static int add_extension(int length, char * dst, const char * ext) {
|
||||
int ext_len;
|
||||
int i;
|
||||
|
||||
if (length <= 1)
|
||||
return 0;
|
||||
|
||||
ext_len = strlen(ext);
|
||||
|
||||
/* check if end reached or not enough room to add */
|
||||
if (ext_len+2 > length-2) {
|
||||
dst[0]='\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* copy new extension + null terminate */
|
||||
for (i=0; i < ext_len; i++)
|
||||
dst[i] = ext[i];
|
||||
dst[i]='/';
|
||||
dst[i+1]='\0';
|
||||
return i+1;
|
||||
}
|
||||
|
||||
/* Creates XMPlay's extension list, a single string with 2 nulls.
|
||||
* Extensions must be in this format: "Description\0extension1/.../extensionN" */
|
||||
void build_extension_list(char* extension_list, int list_size, DWORD version) {
|
||||
const char ** ext_list;
|
||||
size_t ext_list_len;
|
||||
int i, written;
|
||||
|
||||
int limit_old = EXTENSION_LIST_SIZE_OLD;
|
||||
int limit = list_size;
|
||||
if (limit > limit_old && version <= EXTENSION_LIST_SIZE_OLD_VERSION)
|
||||
limit = limit_old;
|
||||
|
||||
written = sprintf(extension_list, "%s%c", "vgmstream files",'\0');
|
||||
|
||||
ext_list = vgmstream_get_formats(&ext_list_len);
|
||||
|
||||
for (i=0; i < ext_list_len; i++) {
|
||||
written += add_extension(limit-written, extension_list + written, ext_list[i]);
|
||||
}
|
||||
extension_list[written-1] = '\0'; /* remove last "/" */
|
||||
}
|
||||
|
||||
|
||||
/* Get tags as an array of "key\0value\0", NULL-terminated.
|
||||
* Processes tags from the path alone (expects folders to be named in a particular way),
|
||||
* so of limited usefulness. */
|
||||
char* get_tags_from_filepath_info(VGMSTREAM* infostream, XMPFUNC_MISC* xmpf_misc, const char* filepath) {
|
||||
char* tags;
|
||||
int pos = 0;
|
||||
|
||||
tags = xmpf_misc->Alloc(1024);
|
||||
memset(tags, 0x00, 1024);
|
||||
|
||||
if (strlen(infostream->stream_name) > 0) {
|
||||
memcpy(tags + pos, "title", 5);
|
||||
pos += 6;
|
||||
memcpy(tags + pos, infostream->stream_name, strlen(infostream->stream_name));
|
||||
pos += strlen(infostream->stream_name) + 1;
|
||||
}
|
||||
|
||||
const char* end;
|
||||
const char* start = NULL;
|
||||
int j = 2;
|
||||
for (const char* i = filepath + strlen(filepath); i > filepath; i--)
|
||||
{
|
||||
if ((*i == '\\') && (j == 1))
|
||||
{
|
||||
start = i + 1;
|
||||
j--;
|
||||
break;
|
||||
}
|
||||
if ((*i == '\\') && (j == 2))
|
||||
{
|
||||
end = i;
|
||||
j--;
|
||||
}
|
||||
}
|
||||
|
||||
//run some sanity checks
|
||||
|
||||
int brace_curly = 0, brace_square = 0;
|
||||
char check_ok = 0;
|
||||
for (const char* i = filepath; *i != 0; i++)
|
||||
{
|
||||
if (*i == '(')
|
||||
brace_curly++;
|
||||
if (*i == ')')
|
||||
brace_curly--;
|
||||
if (*i == '[')
|
||||
brace_square++;
|
||||
if (*i == ']')
|
||||
brace_square--;
|
||||
if (brace_curly > 1 || brace_curly < -1 || brace_square > 1 || brace_square < -1)
|
||||
break;
|
||||
}
|
||||
|
||||
if (brace_square == 0 && brace_curly == 0)
|
||||
check_ok = 1;
|
||||
|
||||
if (start != NULL && strstr(filepath, "\\VGMPP\\") != NULL && check_ok == 1 && strchr(start, '(') != NULL)
|
||||
{
|
||||
char tagline[1024];
|
||||
memset(tagline, 0x00, sizeof(tagline));
|
||||
strncpy(tagline, start, end - start);
|
||||
|
||||
char* alttitle_st;
|
||||
char* alttitle_ed;
|
||||
char* album_st;
|
||||
char* album_ed;
|
||||
char* company_st;
|
||||
char* company_ed;
|
||||
char* company2_st;
|
||||
char* company2_ed;
|
||||
char* date_st;
|
||||
char* date_ed;
|
||||
char* platform_st;
|
||||
char* platform_ed;
|
||||
|
||||
if (strchr(tagline, '[') != NULL) //either alternative title or platform usually
|
||||
{
|
||||
alttitle_st = strchr(tagline, '[') + 1;
|
||||
alttitle_ed = strchr(alttitle_st, ']');
|
||||
if (strchr(alttitle_st, '[') != NULL && strchr(alttitle_st, '[') > strchr(alttitle_st, '(')) //both might be present actually
|
||||
{
|
||||
platform_st = strchr(alttitle_st, '[') + 1;
|
||||
platform_ed = strchr(alttitle_ed + 1, ']');
|
||||
}
|
||||
else
|
||||
{
|
||||
platform_st = NULL;
|
||||
platform_ed = NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
platform_st = NULL;
|
||||
platform_ed = NULL;
|
||||
alttitle_st = NULL;
|
||||
alttitle_ed = NULL;
|
||||
}
|
||||
|
||||
album_st = tagline;
|
||||
|
||||
if (strchr(tagline, '(') < alttitle_st && alttitle_st != NULL) //square braces after curly braces -- platform
|
||||
{
|
||||
platform_st = alttitle_st;
|
||||
platform_ed = alttitle_ed;
|
||||
alttitle_st = NULL;
|
||||
alttitle_ed = NULL;
|
||||
album_ed = strchr(tagline, '('); //get normal title for now
|
||||
}
|
||||
else if (alttitle_st != NULL)
|
||||
album_ed = strchr(tagline, '[');
|
||||
else
|
||||
album_ed = strchr(tagline, '(');
|
||||
|
||||
date_st = strchr(album_ed, '(') + 1; //first string in curly braces is usualy release date, I have one package with platform name there
|
||||
if (date_st == NULL)
|
||||
date_ed = NULL;
|
||||
if (date_st[0] >= 0x30 && date_st[0] <= 0x39 && date_st[1] >= 0x30 && date_st[1] <= 0x39) //check if it contains 2 digits
|
||||
{
|
||||
date_ed = strchr(date_st, ')');
|
||||
}
|
||||
else //platform?
|
||||
{
|
||||
platform_st = date_st;
|
||||
platform_ed = strchr(date_st, ')');
|
||||
date_st = strchr(platform_ed, '(') + 1;
|
||||
date_ed = strchr(date_st, ')');
|
||||
}
|
||||
|
||||
company_st = strchr(date_ed, '(') + 1; //company name follows date
|
||||
if (company_st != NULL)
|
||||
{
|
||||
company_ed = strchr(company_st, ')');
|
||||
if (strchr(company_ed, '(') != NULL)
|
||||
{
|
||||
company2_st = strchr(company_ed, '(') + 1;
|
||||
company2_ed = strchr(company2_st, ')');
|
||||
}
|
||||
else
|
||||
{
|
||||
company2_st = NULL;
|
||||
company2_ed = NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
company_st = NULL;
|
||||
company_ed = NULL;
|
||||
company2_st = NULL;
|
||||
company2_ed = NULL;
|
||||
}
|
||||
|
||||
if (alttitle_st != NULL) //prefer alternative title, which is usually japanese
|
||||
{
|
||||
memcpy(tags + pos, "album", 5);
|
||||
pos += 6;
|
||||
memcpy(tags + pos, alttitle_st, alttitle_ed - alttitle_st);
|
||||
pos += alttitle_ed - alttitle_st + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(tags + pos, "album", 5);
|
||||
pos += 6;
|
||||
memcpy(tags + pos, album_st, album_ed - album_st);
|
||||
pos += album_ed - album_st + 1;
|
||||
}
|
||||
|
||||
if (date_st != NULL)
|
||||
{
|
||||
memcpy(tags + pos, "date", 4);
|
||||
pos += 5;
|
||||
memcpy(tags + pos, date_st, date_ed - date_st);
|
||||
pos += date_ed - date_st + 1;
|
||||
}
|
||||
|
||||
if (date_st != NULL)
|
||||
{
|
||||
memcpy(tags + pos, "genre", 5);
|
||||
pos += 6;
|
||||
memcpy(tags + pos, platform_st, platform_ed - platform_st);
|
||||
pos += platform_ed - platform_st + 1;
|
||||
}
|
||||
|
||||
if (company_st != NULL)
|
||||
{
|
||||
char combuf[256];
|
||||
memset(combuf, 0x00, sizeof(combuf));
|
||||
char tmp[128];
|
||||
memset(tmp, 0x00, sizeof(tmp));
|
||||
memcpy(tmp, company_st, company_ed - company_st);
|
||||
if (company2_st != NULL)
|
||||
{
|
||||
char tmp2[128];
|
||||
memset(tmp2, 0x00, sizeof(tmp2));
|
||||
memcpy(tmp2, company2_st, company2_ed - company2_st);
|
||||
sprintf(combuf, "\r\n\r\nDeveloper\t%s\r\nPublisher\t%s", tmp, tmp2);
|
||||
}
|
||||
else
|
||||
sprintf(combuf, "\r\n\r\nDeveloper\t%s", tmp);
|
||||
|
||||
memcpy(tags + pos, "comment", 7);
|
||||
pos += 8;
|
||||
memcpy(tags + pos, combuf, strlen(combuf));
|
||||
pos += strlen(combuf) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return tags; /* assuming XMPlay free()s this, since it Alloc()s it */
|
||||
}
|
@ -11,10 +11,7 @@
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "xmpin.h"
|
||||
#include "../src/vgmstream.h"
|
||||
#include "../src/api.h"
|
||||
|
||||
#include "xmp_vgmstream.h"
|
||||
|
||||
#include "../version.h"
|
||||
#ifndef VGMSTREAM_VERSION
|
||||
@ -28,20 +25,19 @@
|
||||
|
||||
#define SAMPLE_BUFFER_SIZE 1024
|
||||
|
||||
/* XMPlay extension list, only needed to associate extensions in Windows */
|
||||
/* with <v3.8.5.62, any more than ~1000 will crash XMplay's file list screen. */
|
||||
#define EXTENSION_LIST_SIZE (0x2000 * 6)
|
||||
#define EXTENSION_LIST_SIZE_OLD 1000
|
||||
#define EXTENSION_LIST_SIZE_OLD_VERSION 0x0308053d /* less than v3.8.5.62 */
|
||||
#define XMPLAY_MAX_PATH 32768
|
||||
|
||||
/* XMPlay function library */
|
||||
static XMPFUNC_IN *xmpfin;
|
||||
static XMPFUNC_MISC *xmpfmisc;
|
||||
static XMPFUNC_FILE *xmpffile;
|
||||
static XMPFUNC_IN* xmpf_in;
|
||||
static XMPFUNC_MISC* xmpf_misc;
|
||||
static XMPFUNC_FILE* xmpf_file;
|
||||
|
||||
/* XMPlay extension list, only needed to associate extensions in Windows */
|
||||
#define EXTENSION_LIST_SIZE (0x2000 * 6)
|
||||
|
||||
char working_extension_list[EXTENSION_LIST_SIZE] = {0};
|
||||
char filepath[MAX_PATH];
|
||||
|
||||
char last_filepath[MAX_PATH] = {0};
|
||||
|
||||
/* plugin config */
|
||||
double fade_seconds = 10.0;
|
||||
@ -66,96 +62,11 @@ static int shownerror = 0; /* init error */
|
||||
|
||||
/* ************************************* */
|
||||
|
||||
/* a STREAMFILE that operates via XMPlay's XMPFUNC_FILE+XMPFILE */
|
||||
typedef struct _XMPLAY_STREAMFILE {
|
||||
STREAMFILE sf; /* callbacks */
|
||||
XMPFILE infile; /* actual FILE */
|
||||
char name[PATH_LIMIT];
|
||||
off_t offset; /* current offset */
|
||||
int internal_xmpfile; /* infile was not supplied externally and can be closed */
|
||||
} XMPLAY_STREAMFILE;
|
||||
|
||||
static STREAMFILE* open_xmplay_streamfile_by_xmpfile(XMPFILE file, const char* path, int internal);
|
||||
|
||||
static size_t xmpsf_read(XMPLAY_STREAMFILE* sf, uint8_t* dst, offv_t offset, size_t length) {
|
||||
size_t read;
|
||||
|
||||
if (sf->offset != offset) {
|
||||
if (xmpffile->Seek(sf->infile, offset))
|
||||
sf->offset = offset;
|
||||
else
|
||||
sf->offset = xmpffile->Tell(sf->infile);
|
||||
}
|
||||
|
||||
read = xmpffile->Read(sf->infile, dst, length);
|
||||
if (read > 0)
|
||||
sf->offset += read;
|
||||
|
||||
return read;
|
||||
}
|
||||
|
||||
static off_t xmpsf_get_size(XMPLAY_STREAMFILE* sf) {
|
||||
return xmpffile->GetSize(sf->infile);
|
||||
}
|
||||
|
||||
static off_t xmpsf_get_offset(XMPLAY_STREAMFILE* sf) {
|
||||
return xmpffile->Tell(sf->infile);
|
||||
}
|
||||
|
||||
static void xmpsf_get_name(XMPLAY_STREAMFILE* sf, char* buffer, size_t length) {
|
||||
strncpy(buffer, sf->name, length);
|
||||
buffer[length-1] = '\0';
|
||||
}
|
||||
|
||||
static STREAMFILE* xmpsf_open(XMPLAY_STREAMFILE* sf, const char* const filename, size_t buffersize) {
|
||||
XMPFILE newfile;
|
||||
|
||||
if (!filename)
|
||||
return NULL;
|
||||
|
||||
newfile = xmpffile->Open(filename);
|
||||
if (!newfile) return NULL;
|
||||
|
||||
strncpy(filepath, filename, MAX_PATH);
|
||||
filepath[MAX_PATH - 1] = 0x00;
|
||||
|
||||
return open_xmplay_streamfile_by_xmpfile(newfile, filename, 1); /* internal XMPFILE */
|
||||
}
|
||||
|
||||
static void xmpsf_close(XMPLAY_STREAMFILE* sf) {
|
||||
/* Close XMPFILE, but only if we opened it (ex. for subfiles inside metas).
|
||||
* Otherwise must be left open as other parts of XMPlay need it and would crash. */
|
||||
if (sf->internal_xmpfile) {
|
||||
xmpffile->Close(sf->infile);
|
||||
}
|
||||
|
||||
free(sf);
|
||||
}
|
||||
|
||||
static STREAMFILE* open_xmplay_streamfile_by_xmpfile(XMPFILE infile, const char* path, int internal) {
|
||||
XMPLAY_STREAMFILE* this_sf = calloc(1, sizeof(XMPLAY_STREAMFILE));
|
||||
if (!this_sf) return NULL;
|
||||
|
||||
this_sf->sf.read = (void*)xmpsf_read;
|
||||
this_sf->sf.get_size = (void*)xmpsf_get_size;
|
||||
this_sf->sf.get_offset = (void*)xmpsf_get_offset;
|
||||
this_sf->sf.get_name = (void*)xmpsf_get_name;
|
||||
this_sf->sf.open = (void*)xmpsf_open;
|
||||
this_sf->sf.close = (void*)xmpsf_close;
|
||||
this_sf->infile = infile;
|
||||
this_sf->offset = 0;
|
||||
strncpy(this_sf->name, path, sizeof(this_sf->name));
|
||||
|
||||
this_sf->internal_xmpfile = internal;
|
||||
|
||||
return &this_sf->sf; /* pointer to STREAMFILE start = rest of the custom data follows */
|
||||
}
|
||||
|
||||
VGMSTREAM* init_vgmstream_xmplay(XMPFILE infile, const char* path, int subsong) {
|
||||
STREAMFILE* sf = NULL;
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
|
||||
sf = open_xmplay_streamfile_by_xmpfile(infile, path, 0); /* external XMPFILE */
|
||||
sf = open_xmplay_streamfile_by_xmpfile(infile, xmpf_file, path, false); /* external XMPFILE */
|
||||
if (!sf) return NULL;
|
||||
|
||||
sf->stream_index = subsong;
|
||||
@ -184,259 +95,6 @@ static void apply_config(VGMSTREAM* vgmstream) {
|
||||
vgmstream_apply_config(vgmstream, &vcfg);
|
||||
}
|
||||
|
||||
|
||||
/* get the tags as an array of "key\0value\0", NULL-terminated */
|
||||
static char *get_tags(VGMSTREAM * infostream) {
|
||||
char* tags;
|
||||
int pos = 0;
|
||||
|
||||
tags = xmpfmisc->Alloc(1024);
|
||||
memset(tags, 0x00, 1024);
|
||||
|
||||
if (infostream->stream_name != NULL && strlen(infostream->stream_name) > 0)
|
||||
{
|
||||
memcpy(tags + pos, "title", 5);
|
||||
pos += 6;
|
||||
memcpy(tags + pos, infostream->stream_name, strlen(infostream->stream_name));
|
||||
pos += strlen(infostream->stream_name) + 1;
|
||||
}
|
||||
|
||||
char* end;
|
||||
char* start = NULL;
|
||||
int j = 2;
|
||||
for (char* i = filepath+strlen(filepath); i > filepath; i--)
|
||||
{
|
||||
if ((*i == '\\') && (j == 1))
|
||||
{
|
||||
start = i + 1;
|
||||
j--;
|
||||
break;
|
||||
}
|
||||
if ((*i == '\\') && (j == 2))
|
||||
{
|
||||
end = i;
|
||||
j--;
|
||||
}
|
||||
}
|
||||
|
||||
//run some sanity checks
|
||||
|
||||
int brace_curly = 0, brace_square = 0;
|
||||
char check_ok = 0;
|
||||
for (char* i = filepath; *i != 0; i++)
|
||||
{
|
||||
if (*i == '(')
|
||||
brace_curly++;
|
||||
if (*i == ')')
|
||||
brace_curly--;
|
||||
if (*i == '[')
|
||||
brace_square++;
|
||||
if (*i == ']')
|
||||
brace_square--;
|
||||
if (brace_curly > 1 || brace_curly < -1 || brace_square > 1 || brace_square < -1)
|
||||
break;
|
||||
}
|
||||
|
||||
if (brace_square == 0 && brace_curly == 0)
|
||||
check_ok = 1;
|
||||
|
||||
if (start != NULL && strstr(filepath, "\\VGMPP\\") != NULL && check_ok == 1 && strchr(start, '(') != NULL)
|
||||
{
|
||||
char tagline[1024];
|
||||
memset(tagline, 0x00, sizeof(tagline));
|
||||
strncpy(tagline, start, end - start);
|
||||
|
||||
char* alttitle_st;
|
||||
char* alttitle_ed;
|
||||
char* album_st;
|
||||
char* album_ed;
|
||||
char* company_st;
|
||||
char* company_ed;
|
||||
char* company2_st;
|
||||
char* company2_ed;
|
||||
char* date_st;
|
||||
char* date_ed;
|
||||
char* platform_st;
|
||||
char* platform_ed;
|
||||
|
||||
if (strchr(tagline, '[') != NULL) //either alternative title or platform usually
|
||||
{
|
||||
alttitle_st = strchr(tagline, '[') + 1;
|
||||
alttitle_ed = strchr(alttitle_st, ']');
|
||||
if (strchr(alttitle_st, '[') != NULL && strchr(alttitle_st, '[') > strchr(alttitle_st, '(')) //both might be present actually
|
||||
{
|
||||
platform_st = strchr(alttitle_st, '[') + 1;
|
||||
platform_ed = strchr(alttitle_ed + 1, ']');
|
||||
}
|
||||
else
|
||||
{
|
||||
platform_st = NULL;
|
||||
platform_ed = NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
platform_st = NULL;
|
||||
platform_ed = NULL;
|
||||
alttitle_st = NULL;
|
||||
alttitle_ed = NULL;
|
||||
}
|
||||
|
||||
album_st = tagline;
|
||||
|
||||
if (strchr(tagline, '(') < alttitle_st && alttitle_st != NULL) //square braces after curly braces -- platform
|
||||
{
|
||||
platform_st = alttitle_st;
|
||||
platform_ed = alttitle_ed;
|
||||
alttitle_st = NULL;
|
||||
alttitle_ed = NULL;
|
||||
album_ed = strchr(tagline, '('); //get normal title for now
|
||||
}
|
||||
else if (alttitle_st != NULL)
|
||||
album_ed = strchr(tagline, '[');
|
||||
else
|
||||
album_ed = strchr(tagline, '(');
|
||||
|
||||
date_st = strchr(album_ed, '(') + 1; //first string in curly braces is usualy release date, I have one package with platform name there
|
||||
if (date_st == NULL)
|
||||
date_ed = NULL;
|
||||
if (date_st[0] >= 0x30 && date_st[0] <= 0x39 && date_st[1] >= 0x30 && date_st[1] <= 0x39) //check if it contains 2 digits
|
||||
{
|
||||
date_ed = strchr(date_st, ')');
|
||||
}
|
||||
else //platform?
|
||||
{
|
||||
platform_st = date_st;
|
||||
platform_ed = strchr(date_st, ')');
|
||||
date_st = strchr(platform_ed, '(') + 1;
|
||||
date_ed = strchr(date_st, ')');
|
||||
}
|
||||
|
||||
company_st = strchr(date_ed, '(') + 1; //company name follows date
|
||||
if (company_st != NULL)
|
||||
{
|
||||
company_ed = strchr(company_st, ')');
|
||||
if (strchr(company_ed, '(') != NULL)
|
||||
{
|
||||
company2_st = strchr(company_ed, '(') + 1;
|
||||
company2_ed = strchr(company2_st, ')');
|
||||
}
|
||||
else
|
||||
{
|
||||
company2_st = NULL;
|
||||
company2_ed = NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
company_st = NULL;
|
||||
company_ed = NULL;
|
||||
company2_st = NULL;
|
||||
company2_ed = NULL;
|
||||
}
|
||||
|
||||
if (alttitle_st != NULL) //prefer alternative title, which is usually japanese
|
||||
{
|
||||
memcpy(tags + pos, "album", 5);
|
||||
pos += 6;
|
||||
memcpy(tags + pos, alttitle_st, alttitle_ed - alttitle_st);
|
||||
pos += alttitle_ed - alttitle_st + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(tags + pos, "album", 5);
|
||||
pos += 6;
|
||||
memcpy(tags + pos, album_st, album_ed - album_st);
|
||||
pos += album_ed - album_st + 1;
|
||||
}
|
||||
|
||||
if (date_st != NULL)
|
||||
{
|
||||
memcpy(tags + pos, "date", 4);
|
||||
pos += 5;
|
||||
memcpy(tags + pos, date_st, date_ed - date_st);
|
||||
pos += date_ed - date_st + 1;
|
||||
}
|
||||
|
||||
if (date_st != NULL)
|
||||
{
|
||||
memcpy(tags + pos, "genre", 5);
|
||||
pos += 6;
|
||||
memcpy(tags + pos, platform_st, platform_ed - platform_st);
|
||||
pos += platform_ed - platform_st + 1;
|
||||
}
|
||||
|
||||
if (company_st != NULL)
|
||||
{
|
||||
char combuf[256];
|
||||
memset(combuf, 0x00, sizeof(combuf));
|
||||
char tmp[128];
|
||||
memset(tmp, 0x00, sizeof(tmp));
|
||||
memcpy(tmp, company_st, company_ed - company_st);
|
||||
if (company2_st != NULL)
|
||||
{
|
||||
char tmp2[128];
|
||||
memset(tmp2, 0x00, sizeof(tmp2));
|
||||
memcpy(tmp2, company2_st, company2_ed - company2_st);
|
||||
sprintf(combuf, "\r\n\r\nDeveloper\t%s\r\nPublisher\t%s", tmp, tmp2);
|
||||
}
|
||||
else
|
||||
sprintf(combuf, "\r\n\r\nDeveloper\t%s", tmp);
|
||||
|
||||
memcpy(tags + pos, "comment", 7);
|
||||
pos += 8;
|
||||
memcpy(tags + pos, combuf, strlen(combuf));
|
||||
pos += strlen(combuf) + 1;
|
||||
}
|
||||
}
|
||||
return tags; /* assuming XMPlay free()s this, since it Alloc()s it */
|
||||
}
|
||||
|
||||
/* Adds ext to XMPlay's extension list. */
|
||||
static int add_extension(int length, char * dst, const char * ext) {
|
||||
int ext_len;
|
||||
int i;
|
||||
|
||||
if (length <= 1)
|
||||
return 0;
|
||||
|
||||
ext_len = strlen(ext);
|
||||
|
||||
/* check if end reached or not enough room to add */
|
||||
if (ext_len+2 > length-2) {
|
||||
dst[0]='\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* copy new extension + null terminate */
|
||||
for (i=0; i < ext_len; i++)
|
||||
dst[i] = ext[i];
|
||||
dst[i]='/';
|
||||
dst[i+1]='\0';
|
||||
return i+1;
|
||||
}
|
||||
|
||||
/* Creates XMPlay's extension list, a single string with 2 nulls.
|
||||
* Extensions must be in this format: "Description\0extension1/.../extensionN" */
|
||||
static void build_extension_list() {
|
||||
const char ** ext_list;
|
||||
size_t ext_list_len;
|
||||
int i, written;
|
||||
|
||||
int limit = EXTENSION_LIST_SIZE;
|
||||
if (xmpfmisc->GetVersion() <= EXTENSION_LIST_SIZE_OLD_VERSION)
|
||||
limit = EXTENSION_LIST_SIZE_OLD;
|
||||
|
||||
written = sprintf(working_extension_list, "%s%c", "vgmstream files",'\0');
|
||||
|
||||
ext_list = vgmstream_get_formats(&ext_list_len);
|
||||
|
||||
for (i=0; i < ext_list_len; i++) {
|
||||
written += add_extension(limit-written, working_extension_list + written, ext_list[i]);
|
||||
}
|
||||
working_extension_list[written-1] = '\0'; /* remove last "/" */
|
||||
}
|
||||
|
||||
/* ************************************* */
|
||||
|
||||
/* info for the "about" button in plugin options */
|
||||
@ -487,6 +145,11 @@ DWORD WINAPI xmplay_GetFileInfo(const char *filename, XMPFILE file, float **leng
|
||||
if (!infostream)
|
||||
return 0;
|
||||
|
||||
char temp_filepath[MAX_PATH];
|
||||
|
||||
snprintf(temp_filepath, sizeof(temp_filepath), "%s", filename);
|
||||
temp_filepath[sizeof(temp_filepath) - 1] = '\0';
|
||||
|
||||
apply_config(infostream);
|
||||
|
||||
//vgmstream_mixing_autodownmix(infostream, downmix_channels);
|
||||
@ -494,7 +157,7 @@ DWORD WINAPI xmplay_GetFileInfo(const char *filename, XMPFILE file, float **leng
|
||||
|
||||
if (length && infostream->sample_rate) {
|
||||
int length_samples = vgmstream_get_samples(infostream);
|
||||
float *lens = (float*)xmpfmisc->Alloc(sizeof(float));
|
||||
float *lens = (float*)xmpf_misc->Alloc(sizeof(float));
|
||||
lens[0] = (float)length_samples / (float)infostream->sample_rate;
|
||||
*length = lens;
|
||||
}
|
||||
@ -503,7 +166,7 @@ DWORD WINAPI xmplay_GetFileInfo(const char *filename, XMPFILE file, float **leng
|
||||
if (disable_subsongs || subsong_count == 0)
|
||||
subsong_count = 1;
|
||||
|
||||
*tags = get_tags(infostream);
|
||||
*tags = get_tags_from_filepath_info(infostream, xmpf_misc, temp_filepath);
|
||||
close_vgmstream(infostream);
|
||||
|
||||
return subsong_count;
|
||||
@ -518,6 +181,9 @@ DWORD WINAPI xmplay_Open(const char *filename, XMPFILE file) {
|
||||
if (!vgmstream)
|
||||
return 0;
|
||||
|
||||
snprintf(last_filepath, sizeof(last_filepath), "%s", filename);
|
||||
last_filepath[sizeof(last_filepath) - 1] = '\0';
|
||||
|
||||
apply_config(vgmstream);
|
||||
|
||||
//vgmstream_mixing_autodownmix(vgmstream, downmix_channels);
|
||||
@ -533,7 +199,7 @@ DWORD WINAPI xmplay_Open(const char *filename, XMPFILE file) {
|
||||
|
||||
if (length_samples) {
|
||||
float length = (float)length_samples / (float)vgmstream->sample_rate;
|
||||
xmpfin->SetLength(length, TRUE);
|
||||
xmpf_in->SetLength(length, TRUE);
|
||||
}
|
||||
|
||||
return 1;
|
||||
@ -553,8 +219,8 @@ void WINAPI xmplay_SetFormat(XMPFORMAT *form) {
|
||||
}
|
||||
|
||||
/* get tags, return NULL to delay title update (OPTIONAL) */
|
||||
char * WINAPI xmplay_GetTags() {
|
||||
return get_tags(vgmstream);
|
||||
char* WINAPI xmplay_GetTags() {
|
||||
return get_tags_from_filepath_info(vgmstream, xmpf_misc, last_filepath);
|
||||
}
|
||||
|
||||
/* main panel info text (short file info) */
|
||||
@ -677,7 +343,7 @@ double WINAPI xmplay_SetPosition(DWORD pos) {
|
||||
/* decode some sample data */
|
||||
DWORD WINAPI xmplay_Process(float* buf, DWORD bufsize) {
|
||||
int32_t i, done, samples_to_do;
|
||||
BOOL do_loop = xmpfin->GetLooping();
|
||||
BOOL do_loop = xmpf_in->GetLooping();
|
||||
float *sbuf = buf;
|
||||
|
||||
|
||||
@ -794,11 +460,12 @@ __declspec(dllexport) XMPIN* WINAPI XMPIN_GetInterface(UINT32 face, InterfacePr
|
||||
return NULL;
|
||||
}
|
||||
|
||||
xmpfin = (XMPFUNC_IN*)faceproc(XMPFUNC_IN_FACE);
|
||||
xmpfmisc = (XMPFUNC_MISC*)faceproc(XMPFUNC_MISC_FACE);
|
||||
xmpffile = (XMPFUNC_FILE*)faceproc(XMPFUNC_FILE_FACE);
|
||||
/* retrieval of xmp helpers */
|
||||
xmpf_in = (XMPFUNC_IN*)faceproc(XMPFUNC_IN_FACE);
|
||||
xmpf_misc = (XMPFUNC_MISC*)faceproc(XMPFUNC_MISC_FACE);
|
||||
xmpf_file = (XMPFUNC_FILE*)faceproc(XMPFUNC_FILE_FACE);
|
||||
|
||||
build_extension_list();
|
||||
build_extension_list(working_extension_list, sizeof(working_extension_list), xmpf_misc->GetVersion());
|
||||
|
||||
return &vgmstream_xmpin;
|
||||
}
|
||||
|
16
xmplay/xmp_vgmstream.h
Normal file
16
xmplay/xmp_vgmstream.h
Normal file
@ -0,0 +1,16 @@
|
||||
#ifndef _XMP_VGMSTREAM_
|
||||
#define _XMP_VGMSTREAM_
|
||||
|
||||
#include "xmpin.h"
|
||||
#include "xmpfunc.h"
|
||||
#include "../src/vgmstream.h"
|
||||
#include "../src/api.h"
|
||||
|
||||
|
||||
STREAMFILE* open_xmplay_streamfile_by_xmpfile(XMPFILE infile, XMPFUNC_FILE* xmpf_file, const char* path, bool internal);
|
||||
|
||||
char* get_tags_from_filepath_info(VGMSTREAM* infostream, XMPFUNC_MISC* xmpf_misc, const char* filepath);
|
||||
|
||||
void build_extension_list(char* extension_list, int list_size, DWORD version);
|
||||
|
||||
#endif
|
Loading…
x
Reference in New Issue
Block a user