mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-01-31 04:13:47 +01:00
adding cd-xa support (preliminary)
git-svn-id: https://vgmstream.svn.sourceforge.net/svnroot/vgmstream@97 51a99a44-fe44-0410-b1ba-c3e57ba2b86b
This commit is contained in:
parent
f39503701a
commit
9e70f0ec5c
65
src/coding/xa_decoder.c
Normal file
65
src/coding/xa_decoder.c
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
#include "coding.h"
|
||||||
|
#include "../util.h"
|
||||||
|
|
||||||
|
const int SH = 4;
|
||||||
|
const int SHC = 10;
|
||||||
|
|
||||||
|
double K0[4] = { 0.0, 0.9375, 1.796875, 1.53125};
|
||||||
|
double K1[4] = { 0.0, 0.0, -0.8125,-0.859375};
|
||||||
|
|
||||||
|
int IK0(int fid)
|
||||||
|
{ return ((int)((-K0[fid]) * (1 << SHC))); }
|
||||||
|
|
||||||
|
int IK1(int fid)
|
||||||
|
{ return ((int)((-K1[fid]) * (1 << SHC))); }
|
||||||
|
|
||||||
|
int CLAMP(int value, int Minim, int Maxim)
|
||||||
|
{
|
||||||
|
if (value < Minim) value = Minim;
|
||||||
|
if (value > Maxim) value = Maxim;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t get_high_nibble=1;
|
||||||
|
|
||||||
|
void decode_xa(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;
|
||||||
|
int HeadTable[8]={0,2,8,10};
|
||||||
|
|
||||||
|
short scale;
|
||||||
|
int i;
|
||||||
|
int32_t sample_count;
|
||||||
|
|
||||||
|
int framesin = first_sample / 28;
|
||||||
|
|
||||||
|
get_high_nibble=!get_high_nibble;
|
||||||
|
|
||||||
|
predict_nr = read_8bit(stream->offset+HeadTable[framesin]+get_high_nibble,stream->streamfile) >> 4;
|
||||||
|
shift_factor = read_8bit(stream->offset+HeadTable[framesin]+get_high_nibble,stream->streamfile) & 0xf;
|
||||||
|
|
||||||
|
first_sample = first_sample % 28;
|
||||||
|
|
||||||
|
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
||||||
|
short sample_byte = (short)read_8bit(stream->offset+16+framesin+(i*4),stream->streamfile);
|
||||||
|
|
||||||
|
scale = ((get_high_nibble ?
|
||||||
|
sample_byte >> 4 :
|
||||||
|
sample_byte & 0x0f)<<12);
|
||||||
|
|
||||||
|
sample = (short)(scale & 0xf000) >> shift_factor;
|
||||||
|
sample <<= SH;
|
||||||
|
sample -= (IK0(predict_nr) * hist1 + (IK1(predict_nr) * hist2)) >> SHC;
|
||||||
|
|
||||||
|
hist2=hist1;
|
||||||
|
hist1=sample;
|
||||||
|
|
||||||
|
sample = CLAMP(sample, -32768 << SH, 32767 << SH);
|
||||||
|
outbuf[sample_count] = (short)(sample >> SH);
|
||||||
|
}
|
||||||
|
|
||||||
|
stream->adpcm_history1_32=hist1;
|
||||||
|
stream->adpcm_history2_32=hist2;
|
||||||
|
}
|
@ -48,6 +48,9 @@ void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM *
|
|||||||
else
|
else
|
||||||
vgmstream->current_block_offset=-1;
|
vgmstream->current_block_offset=-1;
|
||||||
break;
|
break;
|
||||||
|
case layout_xa_blocked:
|
||||||
|
xa_block_update(vgmstream->next_block_offset,vgmstream);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
47
src/layout/xa_blocked.c
Normal file
47
src/layout/xa_blocked.c
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#include "layout.h"
|
||||||
|
#include "../vgmstream.h"
|
||||||
|
|
||||||
|
/* set up for the block at the given offset */
|
||||||
|
void xa_block_update(off_t block_offset, VGMSTREAM * vgmstream) {
|
||||||
|
|
||||||
|
int i;
|
||||||
|
uint8_t currentChannel=0;
|
||||||
|
uint8_t subAudio=0;
|
||||||
|
|
||||||
|
// i used interleave_block_size to check sector read length
|
||||||
|
if(vgmstream->samples_into_block!=0)
|
||||||
|
// don't change this variable in the init process
|
||||||
|
vgmstream->interleave_block_size+=128;
|
||||||
|
|
||||||
|
// We get to the end of a sector ?
|
||||||
|
if(vgmstream->interleave_block_size==(18*128)) {
|
||||||
|
vgmstream->interleave_block_size=0;
|
||||||
|
|
||||||
|
// 0x30 of unused bytes/sector :(
|
||||||
|
block_offset+=0x30;
|
||||||
|
begin:
|
||||||
|
// Search for selected channel & valid audio
|
||||||
|
currentChannel=read_8bit(block_offset-7,vgmstream->ch[0].streamfile);
|
||||||
|
subAudio=read_8bit(block_offset-6,vgmstream->ch[0].streamfile);
|
||||||
|
|
||||||
|
// audio is coded as 0x64
|
||||||
|
if((subAudio!=0x64) || (currentChannel!=vgmstream->xa_channel)) {
|
||||||
|
// go to next sector
|
||||||
|
block_offset+=2352;
|
||||||
|
if(currentChannel!=-1) goto begin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vgmstream->current_block_offset = block_offset;
|
||||||
|
|
||||||
|
// Quid : how to stop the current channel ???
|
||||||
|
// i set up 0 to current_block_size to make vgmstream not playing bad samples
|
||||||
|
// another way to do it ???
|
||||||
|
// (as the number of samples can be false in cd-xa due to multi-channels)
|
||||||
|
vgmstream->current_block_size = (currentChannel==-1?0:112);
|
||||||
|
|
||||||
|
vgmstream->next_block_offset = vgmstream->current_block_offset+128;
|
||||||
|
for (i=0;i<vgmstream->channels;i++) {
|
||||||
|
vgmstream->ch[i].offset = vgmstream->current_block_offset;
|
||||||
|
}
|
||||||
|
}
|
@ -250,6 +250,10 @@
|
|||||||
RelativePath=".\meta\ps2_npsf.c"
|
RelativePath=".\meta\ps2_npsf.c"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\meta\psx_cdxa.c"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath=".\meta\rs03.c"
|
RelativePath=".\meta\rs03.c"
|
||||||
>
|
>
|
||||||
@ -314,6 +318,10 @@
|
|||||||
RelativePath=".\coding\psx_decoder.c"
|
RelativePath=".\coding\psx_decoder.c"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\coding\xa_decoder.c"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
</Filter>
|
</Filter>
|
||||||
</Filter>
|
</Filter>
|
||||||
<Filter
|
<Filter
|
||||||
@ -350,6 +358,10 @@
|
|||||||
RelativePath=".\layout\nolayout.c"
|
RelativePath=".\layout\nolayout.c"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\layout\xa_blocked.c"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
</Filter>
|
</Filter>
|
||||||
</Filter>
|
</Filter>
|
||||||
</Files>
|
</Files>
|
||||||
|
@ -35,4 +35,6 @@ VGMSTREAM * init_vgmstream_rsf(const char * const filename);
|
|||||||
|
|
||||||
VGMSTREAM * init_vgmstream_rwsd(const char * const filename);
|
VGMSTREAM * init_vgmstream_rwsd(const char * const filename);
|
||||||
|
|
||||||
|
VGMSTREAM * init_vgmstream_cdxa(const char * const filename);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
106
src/meta/psx_cdxa.c
Normal file
106
src/meta/psx_cdxa.c
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
#include "meta.h"
|
||||||
|
#include "../util.h"
|
||||||
|
|
||||||
|
/* Sony PSX CD-XA */
|
||||||
|
/* No looped file ! */
|
||||||
|
|
||||||
|
uint8_t AUDIO_CODING_GET_STEREO(uint8_t value) {
|
||||||
|
return (uint8_t)(value & 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t AUDIO_CODING_GET_FREQ(uint8_t value) {
|
||||||
|
return (uint8_t)((value >> 2) & 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
VGMSTREAM * init_vgmstream_cdxa(const char * const filename) {
|
||||||
|
VGMSTREAM * vgmstream = NULL;
|
||||||
|
STREAMFILE * infile = NULL;
|
||||||
|
|
||||||
|
int channel_count;
|
||||||
|
uint8_t bCoding;
|
||||||
|
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* check extension, case insensitive */
|
||||||
|
if (strcasecmp("xa",filename_extension(filename))) goto fail;
|
||||||
|
|
||||||
|
/* try to open the file for header reading */
|
||||||
|
infile = open_streamfile(filename);
|
||||||
|
if (!infile) goto fail;
|
||||||
|
|
||||||
|
/* check RIFF Header */
|
||||||
|
if (!((read_32bitBE(0x00,infile) == 0x52494646) &&
|
||||||
|
(read_32bitBE(0x08,infile) == 0x43445841) &&
|
||||||
|
(read_32bitBE(0x0C,infile) == 0x666D7420)))
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
bCoding = read_8bit(0x3F,infile);
|
||||||
|
|
||||||
|
switch (AUDIO_CODING_GET_STEREO(bCoding)) {
|
||||||
|
case 0: channel_count = 1; break;
|
||||||
|
case 1: channel_count = 2; break;
|
||||||
|
default: channel_count = 0; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* build the VGMSTREAM */
|
||||||
|
vgmstream = allocate_vgmstream(channel_count,0);
|
||||||
|
if (!vgmstream) goto fail;
|
||||||
|
|
||||||
|
/* fill in the vital statistics */
|
||||||
|
vgmstream->channels = channel_count;
|
||||||
|
|
||||||
|
switch (AUDIO_CODING_GET_FREQ(bCoding)) {
|
||||||
|
case 0: vgmstream->sample_rate = 37800; break;
|
||||||
|
case 1: vgmstream->sample_rate = 19800; break;
|
||||||
|
default: vgmstream->sample_rate = 0; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for Compression Scheme */
|
||||||
|
vgmstream->coding_type = coding_XA;
|
||||||
|
vgmstream->num_samples = (int32_t)((((get_streamfile_size(infile) - 0x3C)/2352)*0x1F80)/(2*channel_count));
|
||||||
|
|
||||||
|
vgmstream->layout_type = layout_xa_blocked;
|
||||||
|
vgmstream->meta_type = meta_PSX_XA;
|
||||||
|
|
||||||
|
close_streamfile(infile); infile=NULL;
|
||||||
|
|
||||||
|
/* open the file for reading by each channel */
|
||||||
|
{
|
||||||
|
for (i=0;i<channel_count;i++) {
|
||||||
|
vgmstream->ch[i].streamfile = open_streamfile_buffer(filename,0x8000);
|
||||||
|
|
||||||
|
if (!vgmstream->ch[i].streamfile) goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
xa_block_update(init_xa_channel(0),vgmstream);
|
||||||
|
|
||||||
|
return vgmstream;
|
||||||
|
|
||||||
|
/* clean up anything we may have opened */
|
||||||
|
fail:
|
||||||
|
if (infile) close_streamfile(infile);
|
||||||
|
if (vgmstream) close_vgmstream(vgmstream);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// in extension, we can use this fonction to set the channel
|
||||||
|
// we want to listen to ...
|
||||||
|
off_t init_xa_channel(int channel,VGMSTREAM* vgmstream) {
|
||||||
|
|
||||||
|
off_t block_offset=0x44;
|
||||||
|
|
||||||
|
uint8_t currentChannel;
|
||||||
|
uint8_t subAudio;
|
||||||
|
|
||||||
|
vgmstream->xa_channel=channel;
|
||||||
|
|
||||||
|
begin:
|
||||||
|
currentChannel=read_8bit(block_offset-7,vgmstream->ch[0].streamfile);
|
||||||
|
subAudio=read_8bit(block_offset-6,vgmstream->ch[0].streamfile);
|
||||||
|
if ((currentChannel!=channel) && (subAudio==0x64)) {
|
||||||
|
block_offset+=2352;
|
||||||
|
goto begin;
|
||||||
|
}
|
||||||
|
return block_offset;
|
||||||
|
}
|
@ -15,7 +15,7 @@
|
|||||||
* List of functions that will recognize files. These should correspond pretty
|
* List of functions that will recognize files. These should correspond pretty
|
||||||
* directly to the metadata types
|
* directly to the metadata types
|
||||||
*/
|
*/
|
||||||
#define INIT_VGMSTREAM_FCNS 16
|
#define INIT_VGMSTREAM_FCNS 17
|
||||||
VGMSTREAM * (*init_vgmstream_fcns[INIT_VGMSTREAM_FCNS])(const char * const) = {
|
VGMSTREAM * (*init_vgmstream_fcns[INIT_VGMSTREAM_FCNS])(const char * const) = {
|
||||||
init_vgmstream_adx,
|
init_vgmstream_adx,
|
||||||
init_vgmstream_brstm,
|
init_vgmstream_brstm,
|
||||||
@ -33,6 +33,7 @@ VGMSTREAM * (*init_vgmstream_fcns[INIT_VGMSTREAM_FCNS])(const char * const) = {
|
|||||||
init_vgmstream_ps2_ads,
|
init_vgmstream_ps2_ads,
|
||||||
init_vgmstream_ps2_npsf,
|
init_vgmstream_ps2_npsf,
|
||||||
init_vgmstream_rwsd,
|
init_vgmstream_rwsd,
|
||||||
|
init_vgmstream_cdxa,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -153,6 +154,7 @@ void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstre
|
|||||||
break;
|
break;
|
||||||
case layout_ast_blocked:
|
case layout_ast_blocked:
|
||||||
case layout_halpst_blocked:
|
case layout_halpst_blocked:
|
||||||
|
case layout_xa_blocked:
|
||||||
render_vgmstream_blocked(buffer,sample_count,vgmstream);
|
render_vgmstream_blocked(buffer,sample_count,vgmstream);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -177,6 +179,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
|
|||||||
case coding_NGC_AFC:
|
case coding_NGC_AFC:
|
||||||
return 16;
|
return 16;
|
||||||
case coding_PSX:
|
case coding_PSX:
|
||||||
|
case coding_XA:
|
||||||
return 28;
|
return 28;
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
@ -213,6 +216,8 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
|
|||||||
return 9;
|
return 9;
|
||||||
case coding_PSX:
|
case coding_PSX:
|
||||||
return 16;
|
return 16;
|
||||||
|
case coding_XA:
|
||||||
|
return 28;
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -302,6 +307,13 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
|
|||||||
samples_to_do);
|
samples_to_do);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case coding_XA:
|
||||||
|
for (chan=0;chan<vgmstream->channels;chan++) {
|
||||||
|
decode_xa(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan,
|
||||||
|
vgmstream->channels,vgmstream->samples_into_block,
|
||||||
|
samples_to_do);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -456,6 +468,7 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) {
|
|||||||
snprintf(temp,TEMPSIZE,"Gamecube \"AFC\" 4-bit ADPCM");
|
snprintf(temp,TEMPSIZE,"Gamecube \"AFC\" 4-bit ADPCM");
|
||||||
break;
|
break;
|
||||||
case coding_PSX:
|
case coding_PSX:
|
||||||
|
case coding_XA:
|
||||||
snprintf(temp,TEMPSIZE,"Playstation 4-bit ADPCM");
|
snprintf(temp,TEMPSIZE,"Playstation 4-bit ADPCM");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -484,6 +497,9 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) {
|
|||||||
break;
|
break;
|
||||||
case layout_halpst_blocked:
|
case layout_halpst_blocked:
|
||||||
snprintf(temp,TEMPSIZE,"HALPST blocked");
|
snprintf(temp,TEMPSIZE,"HALPST blocked");
|
||||||
|
break;
|
||||||
|
case layout_xa_blocked:
|
||||||
|
snprintf(temp,TEMPSIZE,"CD-XA");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
snprintf(temp,TEMPSIZE,"INCONCEIVABLE");
|
snprintf(temp,TEMPSIZE,"INCONCEIVABLE");
|
||||||
@ -563,6 +579,9 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) {
|
|||||||
case meta_RWSD:
|
case meta_RWSD:
|
||||||
snprintf(temp,TEMPSIZE,"Nintendo RWSD header (single stream)");
|
snprintf(temp,TEMPSIZE,"Nintendo RWSD header (single stream)");
|
||||||
break;
|
break;
|
||||||
|
case meta_PSX_XA:
|
||||||
|
snprintf(temp,TEMPSIZE,"RIFF/CDXA Header");
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
snprintf(temp,TEMPSIZE,"THEY SHOULD HAVE SENT A POET");
|
snprintf(temp,TEMPSIZE,"THEY SHOULD HAVE SENT A POET");
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ typedef enum {
|
|||||||
coding_G721, /* CCITT G.721 ADPCM */
|
coding_G721, /* CCITT G.721 ADPCM */
|
||||||
coding_NGC_AFC, /* NGC ADPCM, called AFC */
|
coding_NGC_AFC, /* NGC ADPCM, called AFC */
|
||||||
coding_PSX, /* PSX & PS2 ADPCM */
|
coding_PSX, /* PSX & PS2 ADPCM */
|
||||||
|
coding_XA, /* PSX CD-XA */
|
||||||
} coding_t;
|
} coding_t;
|
||||||
|
|
||||||
/* The layout type specifies how the sound data is laid out in the file */
|
/* The layout type specifies how the sound data is laid out in the file */
|
||||||
@ -38,6 +39,7 @@ typedef enum {
|
|||||||
/* headered blocks */
|
/* headered blocks */
|
||||||
layout_ast_blocked, /* .ast STRM with BLCK blocks*/
|
layout_ast_blocked, /* .ast STRM with BLCK blocks*/
|
||||||
layout_halpst_blocked, /* blocks with HALPST-format header */
|
layout_halpst_blocked, /* blocks with HALPST-format header */
|
||||||
|
layout_xa_blocked,
|
||||||
#if 0
|
#if 0
|
||||||
layout_strm_blocked, /* */
|
layout_strm_blocked, /* */
|
||||||
#endif
|
#endif
|
||||||
@ -74,6 +76,8 @@ typedef enum {
|
|||||||
|
|
||||||
meta_PS2_SShd, /* .ADS with SShd header */
|
meta_PS2_SShd, /* .ADS with SShd header */
|
||||||
meta_PS2_NPSF, /* Namco Production Sound File */
|
meta_PS2_NPSF, /* Namco Production Sound File */
|
||||||
|
|
||||||
|
meta_PSX_XA,
|
||||||
} meta_t;
|
} meta_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@ -151,6 +155,7 @@ typedef struct {
|
|||||||
size_t loop_block_size; /* saved from current_block_size */
|
size_t loop_block_size; /* saved from current_block_size */
|
||||||
off_t loop_next_block_offset; /* saved from next_block_offset */
|
off_t loop_next_block_offset; /* saved from next_block_offset */
|
||||||
|
|
||||||
|
uint8_t xa_channel; /* Selected XA Channel */
|
||||||
} VGMSTREAM;
|
} VGMSTREAM;
|
||||||
|
|
||||||
/* do format detection, return pointer to a usable VGMSTREAM, or NULL on failure */
|
/* do format detection, return pointer to a usable VGMSTREAM, or NULL on failure */
|
||||||
|
@ -45,7 +45,7 @@ int fade_samples = 0;
|
|||||||
|
|
||||||
#define EXTENSION_LIST_SIZE 1024
|
#define EXTENSION_LIST_SIZE 1024
|
||||||
char working_extension_list[EXTENSION_LIST_SIZE] = {0};
|
char working_extension_list[EXTENSION_LIST_SIZE] = {0};
|
||||||
#define EXTENSION_COUNT 15
|
#define EXTENSION_COUNT 16
|
||||||
char * extension_list[EXTENSION_COUNT] = {
|
char * extension_list[EXTENSION_COUNT] = {
|
||||||
"adx\0ADX Audio File (*.ADX)\0",
|
"adx\0ADX Audio File (*.ADX)\0",
|
||||||
"afc\0AFC Audio File (*.AFC)\0",
|
"afc\0AFC Audio File (*.AFC)\0",
|
||||||
@ -62,6 +62,7 @@ char * extension_list[EXTENSION_COUNT] = {
|
|||||||
"ss2\0PS2 SS2 Audio File (*.SS2)\0",
|
"ss2\0PS2 SS2 Audio File (*.SS2)\0",
|
||||||
"npsf\0PS2 NPSF Audio File (*.NPSF)\0",
|
"npsf\0PS2 NPSF Audio File (*.NPSF)\0",
|
||||||
"rwsd\0RWSD Audio File (*.RWSD)\0",
|
"rwsd\0RWSD Audio File (*.RWSD)\0",
|
||||||
|
"xa\0PSX CD-XA File (*.XA)\0",
|
||||||
};
|
};
|
||||||
|
|
||||||
/* stubs, we don't do anything fancy yet */
|
/* stubs, we don't do anything fancy yet */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user