mirror of
https://github.com/RetroDECK/Supermodel.git
synced 2024-11-27 08:05:41 +00:00
409 lines
9.3 KiB
C++
409 lines
9.3 KiB
C++
/**
|
|
** Supermodel
|
|
** A Sega Model 3 Arcade Emulator.
|
|
** Copyright 2011 Bart Trzynadlowski, Nik Henson
|
|
**
|
|
** This file is part of Supermodel.
|
|
**
|
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
|
** the terms of the GNU General Public License as published by the Free
|
|
** Software Foundation, either version 3 of the License, or (at your option)
|
|
** any later version.
|
|
**
|
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
** more details.
|
|
**
|
|
** You should have received a copy of the GNU General Public License along
|
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
|
**/
|
|
|
|
/*
|
|
* getbits.cpp
|
|
*
|
|
* Amp library internal module.
|
|
*/
|
|
|
|
|
|
/* this file is a part of amp software, (C) tomislav uzelac 1996,1997
|
|
*/
|
|
|
|
/* getbits.c bit level routines, input buffer
|
|
*
|
|
* Created by: tomislav uzelac Apr 1996
|
|
* better synchronization, tomislav uzelac, Apr 23 1997
|
|
*/
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "amp.h"
|
|
#include "audio.h"
|
|
#include "formats.h"
|
|
#include "rtbuf.h"
|
|
|
|
#define GETBITS
|
|
#include "getbits.h"
|
|
|
|
static const char *fstart, *lstart;
|
|
static int offset, end, eof, lend;
|
|
|
|
void MPEG_GetPlayPosition(int *playOffset, int *endOffset)
|
|
{
|
|
if (in_file)
|
|
*playOffset = ftell(in_file);
|
|
else
|
|
*playOffset = offset;
|
|
*endOffset = end;
|
|
}
|
|
|
|
void MPEG_SetPlayPosition(int playOffset, int endOffset)
|
|
{
|
|
offset = playOffset;
|
|
end = endOffset;
|
|
}
|
|
|
|
void m1setfile(const char *mstart, int mend)
|
|
{
|
|
fstart = mstart;
|
|
offset = 0;
|
|
eof = 0;
|
|
end = mend;
|
|
lstart = NULL;
|
|
}
|
|
|
|
void MPEG_SetLoop(const char *loop, int loopend)
|
|
{
|
|
lstart = loop;
|
|
lend = loopend;
|
|
}
|
|
|
|
int m1fread(unsigned char *buf, int size1, int size2, void *f)
|
|
{
|
|
int total = size1 * size2;
|
|
|
|
if (in_file) return fread(buf, size1, size2, (FILE *) f);
|
|
|
|
//printf("fstart=%X, total=%X, offset=%X, end=%X\n", fstart, total, offset, end);
|
|
|
|
// if past EOF
|
|
if ((total + offset) >= end)
|
|
{
|
|
if (lstart == NULL)
|
|
{
|
|
total = end - offset;
|
|
eof = 1;
|
|
}
|
|
else
|
|
{
|
|
// if past the end, do the xfer in 2 pieces
|
|
if ((total + offset) > end)
|
|
{
|
|
memcpy(buf, fstart + offset, end-offset);
|
|
buf += (end-offset);
|
|
total -= (end-offset);
|
|
}
|
|
|
|
fstart = lstart;
|
|
offset = 0;
|
|
end = lend;
|
|
}
|
|
}
|
|
|
|
memcpy(buf, fstart + offset, total);
|
|
|
|
offset += total;
|
|
|
|
return total;
|
|
}
|
|
|
|
int m1feof(void *f)
|
|
{
|
|
if (in_file) return feof((FILE *)f);
|
|
return eof;
|
|
}
|
|
|
|
int m1fseek(void *f, int offs, int whence)
|
|
{
|
|
if (in_file) return fseek((FILE *) f, offs, whence);
|
|
|
|
switch (whence)
|
|
{
|
|
case SEEK_CUR:
|
|
if ((offset + offs) < 0)
|
|
{
|
|
offset = 0;
|
|
eof = 0;
|
|
return -1;
|
|
}
|
|
if ((offset + offs) > end)
|
|
{
|
|
offset = end;
|
|
eof = 1;
|
|
return end;
|
|
}
|
|
offset += offs;
|
|
eof = 0;
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* buffer and bit manipulation functions ***************************************
|
|
*/
|
|
static inline int _fillbfr(unsigned int size)
|
|
{
|
|
_bptr=0;
|
|
return get_input(_buffer, size);
|
|
}
|
|
|
|
static inline int readsync()
|
|
{
|
|
_bptr=0;
|
|
_buffer[0]=_buffer[1];
|
|
_buffer[1]=_buffer[2];
|
|
_buffer[2]=_buffer[3];
|
|
return get_input(&_buffer[3],1);
|
|
}
|
|
|
|
static inline unsigned int _getbits(int n)
|
|
{
|
|
unsigned int pos,ret_value;
|
|
|
|
pos = _bptr >> 3;
|
|
ret_value = _buffer[pos] << 24 |
|
|
_buffer[pos+1] << 16 |
|
|
_buffer[pos+2] << 8 |
|
|
_buffer[pos+3];
|
|
ret_value <<= _bptr & 7;
|
|
ret_value >>= 32 - n;
|
|
_bptr += n;
|
|
return ret_value;
|
|
}
|
|
|
|
int fillbfr(unsigned int advance)
|
|
{
|
|
int overflow,retval;
|
|
|
|
retval=get_input(&buffer[append], advance);
|
|
|
|
if ( append + advance >= BUFFER_SIZE ) {
|
|
overflow = append + advance - BUFFER_SIZE;
|
|
memcpy (buffer,&buffer[BUFFER_SIZE], overflow);
|
|
if (overflow < 4) memcpy(&buffer[BUFFER_SIZE],buffer,4);
|
|
append = overflow;
|
|
} else {
|
|
if (append==0) memcpy(&buffer[BUFFER_SIZE],buffer,4);
|
|
append+=advance;
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
unsigned int getbits(int n)
|
|
{
|
|
if (n) {
|
|
unsigned int pos,ret_value;
|
|
|
|
pos = data >> 3;
|
|
ret_value = buffer[pos] << 24 |
|
|
buffer[pos+1] << 16 |
|
|
buffer[pos+2] << 8 |
|
|
buffer[pos+3];
|
|
ret_value <<= data & 7;
|
|
ret_value >>= 32 - n;
|
|
|
|
data += n;
|
|
data &= (8*BUFFER_SIZE)-1;
|
|
|
|
return ret_value;
|
|
} else
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* header and side info parsing stuff ******************************************
|
|
*/
|
|
static inline void parse_header(struct AUDIO_HEADER *header)
|
|
{
|
|
header->ID=_getbits(1);
|
|
header->layer=_getbits(2);
|
|
header->protection_bit=_getbits(1);
|
|
header->bitrate_index=_getbits(4);
|
|
header->sampling_frequency=_getbits(2);
|
|
header->padding_bit=_getbits(1);
|
|
header->private_bit=_getbits(1);
|
|
header->mode=_getbits(2);
|
|
header->mode_extension=_getbits(2);
|
|
if (!header->mode) header->mode_extension=0;
|
|
header->copyright=_getbits(1);
|
|
header->original=_getbits(1);
|
|
header->emphasis=_getbits(2);
|
|
}
|
|
|
|
static inline int header_sanity_check(struct AUDIO_HEADER *header)
|
|
{
|
|
if ( header->layer==0 ||
|
|
header->bitrate_index==15 ||
|
|
header->sampling_frequency==3) return -1;
|
|
|
|
/* an additional check to make shure that stuffing never gets mistaken
|
|
* for a syncword. This rules out some legal layer1 streams, but who
|
|
* cares about layer1 anyway :-). I must get this right sometime.
|
|
*/
|
|
if ( header->ID==1 && header->layer==3 && header->protection_bit==1) return -1;
|
|
return 0;
|
|
}
|
|
|
|
|
|
int gethdr(struct AUDIO_HEADER *header)
|
|
{
|
|
int s,retval;
|
|
struct AUDIO_HEADER tmp;
|
|
|
|
/* TODO: add a simple byte counter to check only first, say, 1024
|
|
* bytes for a new header and then return GETHDR_SYN
|
|
*/
|
|
if ((retval=_fillbfr(4))!=0) return retval;
|
|
|
|
for(;;) {
|
|
while ((s=_getbits(12)) != 0xfff) {
|
|
if (s==0xffe) {
|
|
parse_header(&tmp);
|
|
if (header_sanity_check(&tmp)==0) return GETHDR_NS;
|
|
}
|
|
if ((retval=readsync())!=0) return retval;
|
|
}
|
|
|
|
parse_header(&tmp);
|
|
if (header_sanity_check(&tmp)!=0) {
|
|
if ((retval=readsync())!=0) return retval;
|
|
} else break;
|
|
}
|
|
|
|
if (tmp.layer==3) return GETHDR_FL1;
|
|
/* if (tmp.layer==2) return GETHDR_FL2; */
|
|
if (tmp.bitrate_index==0) return GETHDR_FF;
|
|
|
|
//printf("layer: %d\n", tmp.layer);
|
|
//printf("sampling frequency: %d\n", tmp.sampling_frequency);
|
|
|
|
memcpy(header,&tmp,sizeof(tmp));
|
|
return 0;
|
|
}
|
|
|
|
/* dummy function, to get crc out of the way
|
|
*/
|
|
void getcrc()
|
|
{
|
|
_fillbfr(2);
|
|
_getbits(16);
|
|
}
|
|
|
|
/* sizes of side_info:
|
|
* MPEG1 1ch 17 2ch 32
|
|
* MPEG2 1ch 9 2ch 17
|
|
*/
|
|
void getinfo(struct AUDIO_HEADER *header,struct SIDE_INFO *info)
|
|
{
|
|
int gr,ch,scfsi_band,region,window;
|
|
int nch;
|
|
if (header->mode==3) {
|
|
nch=1;
|
|
if (header->ID) {
|
|
_fillbfr(17);
|
|
info->main_data_begin=_getbits(9);
|
|
_getbits(5);
|
|
} else {
|
|
_fillbfr(9);
|
|
info->main_data_begin=_getbits(8);
|
|
_getbits(1);
|
|
}
|
|
} else {
|
|
nch=2;
|
|
if (header->ID) {
|
|
_fillbfr(32);
|
|
info->main_data_begin=_getbits(9);
|
|
_getbits(3);
|
|
} else {
|
|
_fillbfr(17);
|
|
info->main_data_begin=_getbits(8);
|
|
_getbits(2);
|
|
}
|
|
}
|
|
|
|
if (header->ID) for (ch=0;ch<nch;ch++)
|
|
for (scfsi_band=0;scfsi_band<4;scfsi_band++)
|
|
info->scfsi[ch][scfsi_band]=_getbits(1);
|
|
|
|
for (gr=0;gr<(header->ID ? 2:1);gr++)
|
|
for (ch=0;ch<nch;ch++) {
|
|
info->part2_3_length[gr][ch]=_getbits(12);
|
|
info->big_values[gr][ch]=_getbits(9);
|
|
info->global_gain[gr][ch]=_getbits(8);
|
|
if (header->ID) info->scalefac_compress[gr][ch]=_getbits(4);
|
|
else info->scalefac_compress[gr][ch]=_getbits(9);
|
|
info->window_switching_flag[gr][ch]=_getbits(1);
|
|
|
|
if (info->window_switching_flag[gr][ch]) {
|
|
info->block_type[gr][ch]=_getbits(2);
|
|
info->mixed_block_flag[gr][ch]=_getbits(1);
|
|
|
|
for (region=0;region<2;region++)
|
|
info->table_select[gr][ch][region]=_getbits(5);
|
|
info->table_select[gr][ch][2]=0;
|
|
|
|
for (window=0;window<3;window++)
|
|
info->subblock_gain[gr][ch][window]=_getbits(3);
|
|
} else {
|
|
for (region=0;region<3;region++)
|
|
info->table_select[gr][ch][region]=_getbits(5);
|
|
|
|
info->region0_count[gr][ch]=_getbits(4);
|
|
info->region1_count[gr][ch]=_getbits(3);
|
|
info->block_type[gr][ch]=0;
|
|
}
|
|
|
|
if (header->ID) info->preflag[gr][ch]=_getbits(1);
|
|
info->scalefac_scale[gr][ch]=_getbits(1);
|
|
info->count1table_select[gr][ch]=_getbits(1);
|
|
}
|
|
return;
|
|
}
|
|
|
|
int dummy_getinfo(int n)
|
|
{
|
|
n-=4;
|
|
if ( m1fseek(in_file,n,SEEK_CUR) != 0)
|
|
{
|
|
if (m1feof(in_file)) return GETHDR_EOF;
|
|
else return GETHDR_ERR;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int rewind_stream(int nbytes)
|
|
{
|
|
nbytes+=5;
|
|
if (m1fseek(in_file, -nbytes, SEEK_CUR) != 0) {
|
|
/* what if we need to be at the very beginning? */
|
|
nbytes--;
|
|
if (m1fseek(in_file, -nbytes, SEEK_CUR) != 0) return GETHDR_ERR;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static inline int get_input(unsigned char* bp, unsigned int size)
|
|
{
|
|
#ifdef LINUX_REALTIME
|
|
return prefetch_get_input(bp,size);
|
|
#else /* LINUX_REALTIME */
|
|
if ( m1fread( bp , 1, size, in_file) != size)
|
|
{
|
|
if (m1feof(in_file)) return GETHDR_EOF;
|
|
else return GETHDR_ERR;
|
|
}
|
|
return 0;
|
|
#endif /* LINUX_REALTIME */
|
|
}
|