From 0f529ea1a7bd594a59aa6d45ad9a2b82de22b5ad Mon Sep 17 00:00:00 2001 From: bnnm Date: Tue, 27 Mar 2018 22:39:05 +0200 Subject: [PATCH] Remove BMDX decoder and use decryption streamfile instead The encryption is built on top of PS ADPCM data, so this way mimics the actual setup and removes a superfluous decoder --- src/coding/coding.h | 1 - src/coding/psx_decoder.c | 46 ------------ src/formats.c | 1 - src/meta/ps2_bmdx.c | 152 ++++++++++++++++++++++----------------- src/vgmstream.c | 10 --- src/vgmstream.h | 5 -- 6 files changed, 88 insertions(+), 127 deletions(-) diff --git a/src/coding/coding.h b/src/coding/coding.h index 4485c515..db8898d5 100644 --- a/src/coding/coding.h +++ b/src/coding/coding.h @@ -76,7 +76,6 @@ size_t pcm_bytes_to_samples(size_t bytes, int channels, int bits_per_sample); /* psx_decoder */ void decode_psx(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_psx_badflags(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_psx_bmdx(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_psx_configurable(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int frame_size); void decode_hevag(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); size_t ps_bytes_to_samples(size_t bytes, int channels); diff --git a/src/coding/psx_decoder.c b/src/coding/psx_decoder.c index a85250ad..4ed91832 100644 --- a/src/coding/psx_decoder.c +++ b/src/coding/psx_decoder.c @@ -110,52 +110,6 @@ void decode_psx(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, stream->adpcm_history2_32=hist2; } -/* encrypted */ -void decode_psx_bmdx(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { - - int predict_nr, shift_factor, sample; - int32_t hist1=stream->adpcm_history1_32; - int32_t hist2=stream->adpcm_history2_32; - - short scale; - int i; - int32_t sample_count; - uint8_t flag; - - int framesin = first_sample/28; - int head = read_8bit(stream->offset+framesin*16,stream->streamfile) ^ stream->bmdx_xor; - - predict_nr = ((head >> 4) & 0xf); - shift_factor = (head & 0xf); - flag = read_8bit(stream->offset+framesin*16+1,stream->streamfile); - - first_sample = first_sample % 28; - - for (i=first_sample,sample_count=0; ioffset+(framesin*16)+2+i/2,stream->streamfile); - if (i/2 == 0) - sample_byte = (short)(int8_t)(sample_byte+stream->bmdx_add); - - scale = ((i&1 ? - sample_byte >> 4 : - sample_byte & 0x0f)<<12); - - sample=(int)((scale >> shift_factor)+hist1*VAG_f[predict_nr][0]+hist2*VAG_f[predict_nr][1]); - } - - outbuf[sample_count] = clamp16(sample); - hist2=hist1; - hist1=sample; - } - stream->adpcm_history1_32=hist1; - stream->adpcm_history2_32=hist2; -} - /* some games have garbage (?) in their flags, this decoder just ignores that byte */ void decode_psx_badflags(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { diff --git a/src/formats.c b/src/formats.c index 3f820f9d..7c2bd671 100644 --- a/src/formats.c +++ b/src/formats.c @@ -465,7 +465,6 @@ static const coding_info coding_info_list[] = { {coding_XA, "CD-ROM XA 4-bit ADPCM"}, {coding_PSX, "Playstation 4-bit ADPCM"}, {coding_PSX_badflags, "Playstation 4-bit ADPCM (bad flags)"}, - {coding_PSX_bmdx, "Playstation 4-bit ADPCM (BMDX encryption)"}, {coding_PSX_cfg, "Playstation 4-bit ADPCM (configurable)"}, {coding_HEVAG, "Playstation Vita HEVAG 4-bit ADPCM"}, diff --git a/src/meta/ps2_bmdx.c b/src/meta/ps2_bmdx.c index a1e735af..fc8c956a 100644 --- a/src/meta/ps2_bmdx.c +++ b/src/meta/ps2_bmdx.c @@ -1,92 +1,116 @@ #include "meta.h" -#include "../util.h" +#include "../coding/coding.h" +static STREAMFILE* setup_bmdx_streamfile(STREAMFILE *streamFile, off_t start_offset, size_t data_size); + + +/* .bmdx - from Beatmania IIDX (PS2) games */ VGMSTREAM * init_vgmstream_ps2_bmdx(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - int loop_flag=0; - int channel_count; + STREAMFILE *temp_streamFile = NULL; off_t start_offset; - int i; + size_t data_size; + int loop_flag, channel_count, encryption; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("bmdx",filename_extension(filename))) goto fail; - /* check NPSF Header */ + /* checks */ + if (!check_extensions(streamFile, "bmdx")) + goto fail; if (read_32bitBE(0x00,streamFile) != 0x01006408 || - read_32bitBE(0x04,streamFile) != 0) + read_32bitBE(0x04,streamFile) != 0x00) goto fail; - /* check loop */ - loop_flag = (read_32bitLE(0x10,streamFile)!=0); - channel_count=read_32bitLE(0x1C,streamFile); - + start_offset = read_32bitLE(0x08,streamFile); + data_size = read_32bitLE(0x0c,streamFile); + loop_flag = (read_32bitLE(0x10,streamFile) != 0x00); + channel_count = read_32bitLE(0x1C,streamFile); + encryption = (read_32bitLE(0x20,streamFile) == 1); + /* 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_32bitLE(0x18,streamFile); - /* Check for Compression Scheme */ - if (read_32bitLE(0x20,streamFile) == 1) - vgmstream->coding_type = coding_PSX_bmdx; - else - vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = read_32bitLE(0x0c,streamFile)*28/16/channel_count; - - /* Get loop point values */ - if(vgmstream->loop_flag) { - vgmstream->loop_start_sample = read_32bitLE(0x10,streamFile)*28/16/channel_count; - vgmstream->loop_end_sample = vgmstream->num_samples; - } - - if (channel_count == 1) { - vgmstream->layout_type = layout_none; - } else if (channel_count > 1) { - vgmstream->interleave_block_size = read_32bitLE(0x24,streamFile); - vgmstream->layout_type = layout_interleave; - } + vgmstream->num_samples = ps_bytes_to_samples(data_size, channel_count); + vgmstream->loop_start_sample = ps_bytes_to_samples(read_32bitLE(0x10,streamFile), channel_count); + vgmstream->loop_end_sample = vgmstream->num_samples; vgmstream->meta_type = meta_PS2_BMDX; + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = (channel_count == 1) ? layout_none : layout_interleave; + vgmstream->interleave_block_size = read_32bitLE(0x24,streamFile); - start_offset = read_32bitLE(0x08,streamFile); - - if (vgmstream->coding_type == coding_PSX_bmdx) - { - uint8_t xor = read_8bit(start_offset,streamFile); - uint8_t add = (~(uint8_t)read_8bit(start_offset+2,streamFile))+1; - int c; - for (c=0;cch[c].bmdx_xor = xor; - vgmstream->ch[c].bmdx_add = add; - } + /* later games are encrypted [beatmaniaIIDX 14 GOLD (PS2)] */ + if (encryption) { + temp_streamFile = setup_bmdx_streamfile(streamFile, start_offset, data_size); + if (!temp_streamFile) goto fail; } - /* open the file for reading by each channel */ - { - for (i=0;ich[0].streamfile) { - vgmstream->ch[0].streamfile = streamFile->open(streamFile,filename,0x8000); - } - vgmstream->ch[i].streamfile = vgmstream->ch[0].streamfile; + if (!vgmstream_open_stream(vgmstream,encryption ? temp_streamFile : streamFile,start_offset)) + goto fail; - if (!vgmstream->ch[i].streamfile) goto fail; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset= - (off_t)(start_offset+vgmstream->interleave_block_size*i); - } - } - + close_streamfile(temp_streamFile); return vgmstream; - /* clean up anything we may have opened */ fail: - if (vgmstream) close_vgmstream(vgmstream); + close_streamfile(temp_streamFile); + close_vgmstream(vgmstream); + return NULL; +} + + +typedef struct { + uint8_t xor; + uint8_t add; + off_t start_offset; + size_t data_size; +} bmdx_decryption_data; + +static size_t bmdx_decryption_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, bmdx_decryption_data* data) { + size_t bytes_read; + int i; + + bytes_read = streamfile->read(streamfile, dest, offset, length); + + /* decrypt data (xor) */ + for (i = 0; i < bytes_read; i++) { + if (offset+i >= data->start_offset && offset+i < data->start_offset + data->data_size) { + if (((offset+i) % 0x10) == 0) /* XOR header byte per frame */ + dest[i] = dest[i] ^ data->xor; + else if (((offset+i) % 0x10) == 2) /* ADD first data byte per frame */ + dest[i] = (uint8_t)(dest[i] + data->add); + } + } + + return bytes_read; +} + +static STREAMFILE* setup_bmdx_streamfile(STREAMFILE *streamFile, off_t start_offset, size_t data_size) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + bmdx_decryption_data io_data = {0}; + size_t io_data_size = sizeof(bmdx_decryption_data); + + /* setup decryption (usually xor=0xFF and add=0x02) */ + io_data.xor = read_8bit(start_offset,streamFile); + io_data.add = (~(uint8_t)read_8bit(start_offset+2,streamFile)) + 0x01; + io_data.start_offset = start_offset; + io_data.data_size = data_size; + + + /* setup custom streamfile */ + new_streamFile = open_wrap_streamfile(streamFile); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, bmdx_decryption_read); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); return NULL; } diff --git a/src/vgmstream.c b/src/vgmstream.c index 2081afc7..82138279 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -1029,7 +1029,6 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { case coding_XA: case coding_PSX: case coding_PSX_badflags: - case coding_PSX_bmdx: case coding_HEVAG: return 28; case coding_PSX_cfg: @@ -1192,7 +1191,6 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { return 0x0e*vgmstream->channels; case coding_PSX: case coding_PSX_badflags: - case coding_PSX_bmdx: case coding_HEVAG: return 0x10; case coding_PSX_cfg: @@ -1518,13 +1516,6 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to samples_to_do); } break; - case coding_PSX_bmdx: - for (chan=0;chanchannels;chan++) { - decode_psx_bmdx(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); - } - break; case coding_HEVAG: for (chan=0;chanchannels;chan++) { decode_hevag(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, @@ -1953,7 +1944,6 @@ int vgmstream_do_loop(VGMSTREAM * vgmstream) { vgmstream->meta_type == meta_DSP_RS03 || vgmstream->meta_type == meta_DSP_CSTR || vgmstream->coding_type == coding_PSX || - vgmstream->coding_type == coding_PSX_bmdx || vgmstream->coding_type == coding_PSX_badflags) { int i; for (i=0;ichannels;i++) { diff --git a/src/vgmstream.h b/src/vgmstream.h index 47109f20..3e0baa9a 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -111,7 +111,6 @@ typedef enum { coding_XA, /* CD-ROM XA */ coding_PSX, /* Sony PS ADPCM (VAG) */ coding_PSX_badflags, /* Sony PS ADPCM with custom flag byte */ - coding_PSX_bmdx, /* Sony PS ADPCM with BMDX encryption */ coding_PSX_cfg, /* Sony PS ADPCM with configurable frame size (FF XI, SGXD type 5, Bizarre Creations) */ coding_HEVAG, /* Sony PSVita ADPCM */ @@ -716,10 +715,6 @@ typedef struct { uint16_t adx_mult; uint16_t adx_add; - /* BMDX encryption */ - uint8_t bmdx_xor; - uint8_t bmdx_add; - /* generic encryption */ uint16_t key_xor; } VGMSTREAMCHANNEL;