Major CHD/chdman update. The CHD version number has been increased

from 4 to 5. This means any diff CHDs will no longer work. If you
absolutely need to keep the data for any existing ones you have,
find both the diff CHD and the original CHD for the game in question 
and upgrade using these commands:

  rename diff\game.dif diff\game-old.dif
  chdman copy -i diff\game-old.dif -ip roms\game.chd -o diff\game.dif -op roms\game.chd -c none

Specifics regarding this change:

Defined a new CHD version 5. New features/behaviors of this version:
  - support for up to 4 codecs; each block can use 1 of the 4
  - new LZMA codec, which tends to do better than zlib overall
  - new FLAC codec, primarily used for CDs (but can be applied anywhere)
  - upgraded AVHuff codec now uses FLAC for encoding audio
  - new Huffman codec, used to catch more nearly-uncompressable blocks
  - compressed CHDs now use a compressed map for significant savings
  - CHDs now are aware of a "unit" size; each hunk holds 1 or more units
     (in general units map to sectors for hard disks/CDs)
  - diff'ing against a parent now diffs at the unit level, greatly
     improving compression

Rewrote and modernized chd.c. CHD versions prior to 3 are unsupported,
and version 3/4 CHDs are only supported for reading. Creating a new
CHD now leaves the file open.  Added methods to read and write at the 
unit and byte level, removing the need to handle this manually. Added
metadata access methods that pass astrings and dynamic_buffers to
simplify the interfaces. A companion class chd_compressor now
implements full multithreaded compression, analyzing and compressing
multiple hunks independently in parallel. Split the codec 
implementations out into a separate file chdcodec.*

Updated harddisk.c and cdrom.c to rely on the caching/byte-level read/
write capabilities of the chd_file class. cdrom.c (and chdman) now also 
pad CDs to 4-frame boundaries instead of hunk boundaries, ensuring that
the same SHA1 hashes are produced regardless of the hunk size.

Rewrote chdman.exe entirely, switching from positional parameters to
proper options. Use "chdman help" to get a list of commands, and
"chdman help <command>" to get help for any particular command. Many 
redundant commands were removed now that additional flexibility is
available. Some basic mappings:

  Old: chdman -createblankhd <out.chd> <cyls> <heads> <secs>
  New: chdman createhd -o <out.chd> -chs <cyls>,<heads>,<secs>

  Old: chdman -createuncomphd <in.raw> <out.chd> ....
  New: chdman createhd -i <in.raw> -o <out.chd> -c none ....

  Old: chdman -verifyfix <in.chd>
  New: chdman verify -i <in.chd> -f

  Old: chdman -merge <parent.chd> <diff.chd> <out.chd>
  New: chdman copy -i <diff.chd> -ip <parent.chd> -o <out.chd>

  Old: chdman -diff <parent.chd> <compare.chd> <diff.chd>
  New: chdman copy -i <compare.chd> -o <diff.chd> -op <parent.chd>

  Old: chdman -update <in.chd> <out.chd>
  New: chdman copy -i <in.chd> -o <out.chd>

Added new core file coretmpl.h to hold core template classes. For now
just one class, dynamic_array<> is defined, which acts like an array
of a given object but which can be appended to and/or resized. Also
defines dynamic_buffer as dynamic_array<UINT8> for holding an 
arbitrary buffer of bytes. Expect to see these used a lot.

Added new core helper hashing.c/.h which defines classes for each of
the common hashing methods and creator classes to wrap the 
computation of these hashes. A future work item is to reimplement
the core emulator hashing code using these.

Split bit buffer helpers out into C++ classes and into their own
public header in bitstream.h.

Updated huffman.c/.h to C++, and changed the interface to make it
more flexible to use in nonstandard ways. Also added huffman compression
of the static tree for slightly better compression rates.

Created flac.c/.h as simplified C++ wrappers around the FLAC interface.
A future work item is to convert the samples sound device to a modern
device and leverage this for reading FLAC files.

Renamed avcomp.* to avhuff.*, updated to C++, and added support for
FLAC as the audio encoding mechanism. The old huffman audio is still
supported for decode only.

Added a variant of core_fload that loads to a dynamic_buffer.

Tweaked winwork.c a bit to not limit the maximum number of processors
unless the work queue was created with the WORK_QUEUE_FLAG_HIGH_FREQ
option. Further adjustments here are likely going to be necessary.

Fixed bug in aviio.c which caused errors when reading some AVI files.
This commit is contained in:
Aaron Giles 2012-02-16 09:47:18 +00:00
parent e6dad37593
commit f0823886a6
52 changed files with 10845 additions and 9695 deletions

12
.gitattributes vendored
View File

@ -1794,26 +1794,34 @@ src/lib/softfloat/softfloat.c svneol=native#text/plain
src/lib/softfloat/softfloat.h svneol=native#text/plain
src/lib/util/astring.c svneol=native#text/plain
src/lib/util/astring.h svneol=native#text/plain
src/lib/util/avcomp.c svneol=native#text/plain
src/lib/util/avcomp.h svneol=native#text/plain
src/lib/util/avhuff.c svneol=native#text/plain
src/lib/util/avhuff.h svneol=native#text/plain
src/lib/util/aviio.c svneol=native#text/plain
src/lib/util/aviio.h svneol=native#text/plain
src/lib/util/bitmap.c svneol=native#text/plain
src/lib/util/bitmap.h svneol=native#text/plain
src/lib/util/bitstream.h svneol=native#text/plain
src/lib/util/cdrom.c svneol=native#text/plain
src/lib/util/cdrom.h svneol=native#text/plain
src/lib/util/chd.c svneol=native#text/plain
src/lib/util/chd.h svneol=native#text/plain
src/lib/util/chdcd.c svneol=native#text/plain
src/lib/util/chdcd.h svneol=native#text/plain
src/lib/util/chdcodec.c svneol=native#text/plain
src/lib/util/chdcodec.h svneol=native#text/plain
src/lib/util/corefile.c svneol=native#text/plain
src/lib/util/corefile.h svneol=native#text/plain
src/lib/util/corestr.c svneol=native#text/plain
src/lib/util/corestr.h svneol=native#text/plain
src/lib/util/coretmpl.h svneol=native#text/plain
src/lib/util/coreutil.c svneol=native#text/plain
src/lib/util/coreutil.h svneol=native#text/plain
src/lib/util/flac.c svneol=native#text/plain
src/lib/util/flac.h svneol=native#text/plain
src/lib/util/harddisk.c svneol=native#text/plain
src/lib/util/harddisk.h svneol=native#text/plain
src/lib/util/hashing.c svneol=native#text/plain
src/lib/util/hashing.h svneol=native#text/plain
src/lib/util/huffman.c svneol=native#text/plain
src/lib/util/huffman.h svneol=native#text/plain
src/lib/util/jedparse.c svneol=native#text/plain

View File

@ -733,10 +733,8 @@ $(sort $(OBJDIRS)):
ifndef EXECUTABLE_DEFINED
# always recompile the version string
$(VERSIONOBJ): $(DRVLIBS) $(LIBOSD) $(LIBCPU) $(LIBEMU) $(LIBSOUND) $(LIBUTIL) $(EXPAT) $(ZLIB) $(7Z_LIB) $(SOFTFLOAT) $(FORMATS_LIB) $(LIBOCORE) $(RESFILE)
$(EMULATOR): $(VERSIONOBJ) $(EMUINFOOBJ) $(DRIVLISTOBJ) $(DEVLISTOBJ) $(DRVLIBS) $(LIBOSD) $(LIBCPU) $(LIBEMU) $(LIBDASM) $(LIBSOUND) $(LIBUTIL) $(EXPAT) $(SOFTFLOAT) $(JPEG_LIB) $(FLAC_LIB) $(7Z_LIB) $(FORMATS_LIB) $(ZLIB) $(LIBOCORE) $(RESFILE)
$(CC) $(CDEFS) $(CFLAGS) -c $(SRC)/version.c -o $(VERSIONOBJ)
@echo Linking $@...
$(LD) $(LDFLAGS) $(LDFLAGSEMULATOR) $^ $(LIBS) -o $@
ifeq ($(TARGETOS),win32)

View File

@ -436,27 +436,20 @@ audit_record *media_auditor::audit_one_disk(const rom_entry *rom)
audit_record &record = m_record_list.append(*global_alloc(audit_record(*rom, audit_record::MEDIA_DISK)));
// open the disk
emu_file *source_file;
chd_file *source;
chd_error err = open_disk_image(m_enumerator.options(), &m_enumerator.driver(), rom, &source_file, &source, NULL);
chd_file source;
chd_error err = chd_error(open_disk_image(m_enumerator.options(), &m_enumerator.driver(), rom, source, NULL));
// if we succeeded, get the hashes
if (err == CHDERR_NONE)
{
static const UINT8 nullhash[20] = { 0 };
chd_header header = *chd_get_header(source);
hash_collection hashes;
// if there's a SHA1 hash, add them to the output hash
if (memcmp(nullhash, header.sha1, sizeof(header.sha1)) != 0)
hashes.add_from_buffer(hash_collection::HASH_SHA1, header.sha1, sizeof(header.sha1));
if (source.sha1() != sha1_t::null)
hashes.add_from_buffer(hash_collection::HASH_SHA1, source.sha1().m_raw, sizeof(source.sha1().m_raw));
// update the actual values
record.set_actual(hashes);
// close the file and release the source
chd_close(source);
global_free(source_file);
}
// compute the final status

View File

@ -38,6 +38,7 @@
***************************************************************************/
#include "emu.h"
#include "chd.h"
#include "emuopts.h"
#include "jedparse.h"
#include "audit.h"
@ -1492,8 +1493,8 @@ void media_identifier::identify_file(const char *name)
m_total++;
// attempt to open as a CHD; fail if not
chd_file *chd;
chd_error err = chd_open(name, CHD_OPEN_READ, NULL, &chd);
chd_file chd;
chd_error err = chd.open(name);
if (err != CHDERR_NONE)
{
mame_printf_info("NOT A CHD\n");
@ -1501,26 +1502,20 @@ void media_identifier::identify_file(const char *name)
return;
}
// fetch the header and close the file
chd_header header = *chd_get_header(chd);
chd_close(chd);
// error on writable CHDs
if (header.flags & CHDFLAGS_IS_WRITEABLE)
if (!chd.compressed())
{
mame_printf_info("is a writeable CHD\n");
return;
}
// otherwise, get the hash collection for this CHD
static const UINT8 nullhash[20] = { 0 };
hash_collection hashes;
if (memcmp(nullhash, header.sha1, sizeof(header.sha1)) != 0)
hashes.add_from_buffer(hash_collection::HASH_SHA1, header.sha1, sizeof(header.sha1));
if (chd.sha1() != sha1_t::null)
hashes.add_from_buffer(hash_collection::HASH_SHA1, chd.sha1().m_raw, sizeof(chd.sha1().m_raw));
// determine whether this file exists
int found = find_by_hash(hashes, header.logicalbytes);
int found = find_by_hash(hashes, chd.logical_bytes());
if (found == 0)
mame_printf_info("NO MATCH\n");
else

View File

@ -59,7 +59,6 @@
#include "profiler.h"
// commonly-referenecd utilities imported from lib/util
#include "chd.h"
#include "palette.h"
#include "unicode.h"

View File

@ -129,10 +129,11 @@ bool cdrom_image_device::call_load()
if (software_entry() == NULL)
{
if (strstr(m_image_name,".chd")) {
err = chd_open_file( image_core_file(), CHD_OPEN_READ, NULL, &chd ); /* CDs are never writeable */
if (strstr(m_image_name,".chd") && is_loaded()) {
err = m_self_chd.open( *image_core_file() ); /* CDs are never writeable */
if ( err )
goto error;
chd = &m_self_chd;
}
} else {
chd = get_disk_handle(device().machine(), device().subtag(tempstring,"cdrom"));
@ -150,8 +151,8 @@ bool cdrom_image_device::call_load()
return IMAGE_INIT_PASS;
error:
if ( chd )
chd_close( chd );
if ( chd && chd == &m_self_chd )
m_self_chd.close( );
if ( err )
seterror( IMAGE_ERROR_UNSPECIFIED, chd_get_error_string( err ) );
return IMAGE_INIT_FAIL;

View File

@ -57,6 +57,7 @@ protected:
virtual void device_config_complete();
virtual void device_start();
chd_file m_self_chd;
cdrom_file *m_cdrom_handle;
image_device_format m_format;
};

View File

@ -146,7 +146,7 @@ bool harddisk_image_device::call_load()
{
int our_result;
our_result = internal_load_hd(NULL);
our_result = internal_load_hd();
/* Check if there is an image_load callback defined */
if ( m_device_image_load )
@ -161,9 +161,9 @@ bool harddisk_image_device::call_load()
bool harddisk_image_device::call_create(int create_format, option_resolution *create_args)
{
int err;
char metadata[256];
UINT32 sectorsize, hunksize;
UINT32 cylinders, heads, sectors, totalsectors;
astring metadata;
cylinders = option_resolution_lookup_int(create_args, 'C');
heads = option_resolution_lookup_int(create_args, 'H');
@ -174,12 +174,20 @@ bool harddisk_image_device::call_create(int create_format, option_resolution *cr
totalsectors = cylinders * heads * sectors;
/* create the CHD file */
err = chd_create_file(image_core_file(), (UINT64)totalsectors * (UINT64)sectorsize, hunksize, CHDCOMPRESSION_NONE, NULL);
chd_codec_type compression[4] = { CHD_CODEC_NONE };
err = m_self_chd.create(*image_core_file(), (UINT64)totalsectors * (UINT64)sectorsize, hunksize, sectorsize, compression);
if (err != CHDERR_NONE)
goto error;
sprintf(metadata, HARD_DISK_METADATA_FORMAT, cylinders, heads, sectors, sectorsize);
return internal_load_hd(metadata);
/* if we created the image and hence, have metadata to set, set the metadata */
metadata.format(HARD_DISK_METADATA_FORMAT, cylinders, heads, sectors, sectorsize);
err = m_self_chd.write_metadata(HARD_DISK_METADATA_TAG, 0, metadata);
m_self_chd.close();
if (err != CHDERR_NONE)
goto error;
return internal_load_hd();
error:
return IMAGE_INIT_FAIL;
@ -201,12 +209,13 @@ void harddisk_image_device::call_unload()
if (m_chd != NULL)
{
chd_close(m_chd);
if (m_self_chd.opened())
m_self_chd.close();
m_chd = NULL;
}
}
int harddisk_image_device::internal_load_hd(const char *metadata)
int harddisk_image_device::internal_load_hd()
{
chd_error err = (chd_error)0;
int is_writeable;
@ -220,8 +229,9 @@ int harddisk_image_device::internal_load_hd(const char *metadata)
do
{
is_writeable = !is_readonly();
m_chd = NULL;
err = chd_open_file(image_core_file(), is_writeable ? CHD_OPEN_READWRITE : CHD_OPEN_READ, NULL, &m_chd);
err = m_self_chd.open(*image_core_file(), is_writeable);
if (err == CHDERR_NONE)
m_chd = &m_self_chd;
/* special case; if we get CHDERR_FILE_NOT_WRITEABLE, make the
* image read only and repeat */
@ -233,14 +243,6 @@ int harddisk_image_device::internal_load_hd(const char *metadata)
if (!m_chd)
goto done;
/* if we created the image and hence, have metadata to set, set the metadata */
if (metadata)
{
err = chd_set_metadata(m_chd, HARD_DISK_METADATA_TAG, 0, metadata, strlen(metadata) + 1, 0);
if (err != CHDERR_NONE)
goto done;
}
/* open the hard disk file */
m_hard_disk_handle = hard_disk_open(m_chd);
if (!m_hard_disk_handle)
@ -252,7 +254,8 @@ done:
/* if we had an error, close out the CHD */
if (m_chd != NULL)
{
chd_close(m_chd);
if (m_self_chd.opened())
m_self_chd.close();
m_chd = NULL;
}

View File

@ -64,9 +64,10 @@ protected:
virtual void device_config_complete();
virtual void device_start();
int internal_load_hd(const char *metadata);
int internal_load_hd();
chd_file *m_chd;
chd_file m_self_chd;
hard_disk_file *m_hard_disk_handle;
image_device_format m_format;

View File

@ -1476,7 +1476,7 @@ static void ide_controller_write(device_t *device, int bank, offs_t offset, int
}
else if (ide->command == IDE_COMMAND_TAITO_GNET_UNLOCK_2)
{
UINT8 key[5] = { 0, 0, 0, 0, 0 };
UINT8 key[5] = { 0 };
int i, bad = 0;
ide->drive[ide->cur_drive].slot->read_key(key);
@ -2073,7 +2073,8 @@ void ide_hdd_device::device_reset()
mame_printf_debug("CHS: %d %d %d\n", m_num_cylinders, m_num_heads, m_num_sectors);
}
// build the features page
if (chd_get_metadata (m_handle, HARD_DISK_IDENT_METADATA_TAG, 0, m_features, IDE_DISK_SECTOR_SIZE, 0, 0, 0) != CHDERR_NONE)
UINT32 metalength;
if (m_handle->read_metadata (HARD_DISK_IDENT_METADATA_TAG, 0, m_features, IDE_DISK_SECTOR_SIZE, metalength) != CHDERR_NONE)
ide_build_features();
}
}
@ -2084,7 +2085,8 @@ void ide_hdd_device::device_reset()
void ide_hdd_device::read_key(UINT8 key[])
{
chd_get_metadata(m_handle, HARD_DISK_KEY_METADATA_TAG, 0, key, 5, 0, 0, 0);
UINT32 metalength;
m_handle->read_metadata(HARD_DISK_KEY_METADATA_TAG, 0, key, 5, metalength);
}
//**************************************************************************
@ -2136,7 +2138,8 @@ void ide_hdd_image_device::device_reset()
if (PRINTF_IDE_COMMANDS) printf("CHS: %d %d %d\n", m_num_cylinders, m_num_heads, m_num_sectors);
}
// build the features page
if (chd_get_metadata (m_handle, HARD_DISK_IDENT_METADATA_TAG, 0, m_features, IDE_DISK_SECTOR_SIZE, 0, 0, 0) != CHDERR_NONE)
UINT32 metalength;
if (m_handle->read_metadata (HARD_DISK_IDENT_METADATA_TAG, 0, m_features, IDE_DISK_SECTOR_SIZE, metalength) != CHDERR_NONE)
ide_build_features();
}
}

View File

@ -39,10 +39,11 @@
#include "emu.h"
#include "laserdsc.h"
#include "avcomp.h"
#include "avhuff.h"
#include "vbiparse.h"
#include "config.h"
#include "render.h"
#include "chd.h"
@ -93,13 +94,13 @@ laserdisc_device::laserdisc_device(const machine_config &mconfig, device_type ty
m_overheight(0),
m_overclip(0, -1, 0, -1),
m_disc(NULL),
m_vbidata(NULL),
m_width(0),
m_height(0),
m_fps_times_1million(0),
m_samplerate(0),
m_readresult(CHDERR_NONE),
m_chdtracks(0),
m_work_queue(osd_work_queue_alloc(WORK_QUEUE_FLAG_IO)),
m_audiosquelch(0),
m_videosquelch(0),
m_fieldnum(0),
@ -134,6 +135,7 @@ laserdisc_device::laserdisc_device(const machine_config &mconfig, device_type ty
laserdisc_device::~laserdisc_device()
{
osd_work_queue_free(m_work_queue);
}
@ -362,7 +364,7 @@ void laserdisc_device::device_stop()
{
// make sure all async operations have completed
if (m_disc != NULL)
chd_async_complete(m_disc);
osd_work_queue_wait(m_work_queue, osd_ticks_per_second() * 10);
// free any textures and palettes
if (m_videotex != NULL)
@ -767,13 +769,13 @@ void laserdisc_device::init_disc()
m_maxtrack = VIRTUAL_LEAD_IN_TRACKS + MAX_TOTAL_TRACKS + VIRTUAL_LEAD_OUT_TRACKS;
if (m_disc != NULL)
{
// require the A/V codec
if (chd_get_header(m_disc)->compression != CHDCOMPRESSION_AV)
// require the A/V codec and nothing else
if (m_disc->compression(0) != CHD_CODEC_AVHUFF || m_disc->compression(1) != CHD_CODEC_NONE)
throw emu_fatalerror("Laserdisc video must be compressed with the A/V codec!");
// read the metadata
char metadata[256];
chd_error err = chd_get_metadata(m_disc, AV_METADATA_TAG, 0, metadata, sizeof(metadata), NULL, NULL, NULL);
astring metadata;
chd_error err = m_disc->read_metadata(AV_METADATA_TAG, 0, metadata);
if (err != CHDERR_NONE)
throw emu_fatalerror("Non-A/V CHD file specified");
@ -789,14 +791,12 @@ void laserdisc_device::init_disc()
throw emu_fatalerror("Laserdisc video must be interlaced!");
// determine the maximum track and allocate a frame buffer
UINT32 totalhunks = chd_get_header(m_disc)->totalhunks;
UINT32 totalhunks = m_disc->hunk_count();
m_chdtracks = totalhunks / 2;
// allocate memory for the precomputed per-frame metadata
m_vbidata = auto_alloc_array(machine(), UINT8, totalhunks * VBI_PACKED_BYTES);
UINT32 vbilength;
err = chd_get_metadata(m_disc, AV_LD_METADATA_TAG, 0, m_vbidata, totalhunks * VBI_PACKED_BYTES, &vbilength, NULL, NULL);
if (err != CHDERR_NONE || vbilength != totalhunks * VBI_PACKED_BYTES)
err = m_disc->read_metadata(AV_LD_METADATA_TAG, 0, m_vbidata);
if (err != CHDERR_NONE || m_vbidata.count() != totalhunks * VBI_PACKED_BYTES)
throw emu_fatalerror("Precomputed VBI metadata missing or incorrect size");
}
m_maxtrack = MAX(m_maxtrack, VIRTUAL_LEAD_IN_TRACKS + VIRTUAL_LEAD_OUT_TRACKS + m_chdtracks);
@ -1011,7 +1011,7 @@ void laserdisc_device::read_track_data()
// cheat and look up the metadata we are about to retrieve
vbi_metadata vbidata = { 0 };
if (m_vbidata != NULL)
if (m_vbidata.count() != 0)
vbi_metadata_unpack(&vbidata, NULL, &m_vbidata[readhunk * VBI_PACKED_BYTES]);
// if we're in the lead-in area, force the VBI data to be standard lead-in
@ -1040,29 +1040,29 @@ void laserdisc_device::read_track_data()
frame->m_lastfield = m_curtrack * 2 + m_fieldnum;
// set the video target information
m_avconfig.video.wrap(&frame->m_bitmap.pix16(m_fieldnum), frame->m_bitmap.width(), frame->m_bitmap.height() / 2, frame->m_bitmap.rowpixels() * 2);
m_avhuff_config.video.wrap(&frame->m_bitmap.pix16(m_fieldnum), frame->m_bitmap.width(), frame->m_bitmap.height() / 2, frame->m_bitmap.rowpixels() * 2);
// set the audio target information
if (m_audiobufin + m_audiomaxsamples <= m_audiobufsize)
{
// if we can fit without wrapping, just read the data directly
m_avconfig.audio[0] = &m_audiobuffer[0][m_audiobufin];
m_avconfig.audio[1] = &m_audiobuffer[1][m_audiobufin];
m_avhuff_config.audio[0] = &m_audiobuffer[0][m_audiobufin];
m_avhuff_config.audio[1] = &m_audiobuffer[1][m_audiobufin];
}
else
{
// otherwise, read to the beginning of the buffer
m_avconfig.audio[0] = &m_audiobuffer[0][0];
m_avconfig.audio[1] = &m_audiobuffer[1][0];
m_avhuff_config.audio[0] = &m_audiobuffer[0][0];
m_avhuff_config.audio[1] = &m_audiobuffer[1][0];
}
// override if we're not decoding
m_avconfig.maxsamples = m_audiomaxsamples;
m_avconfig.actsamples = &m_audiocursamples;
m_avhuff_config.maxsamples = m_audiomaxsamples;
m_avhuff_config.actsamples = &m_audiocursamples;
m_audiocursamples = 0;
// set the VBI data for the new field from our precomputed data
if (m_vbidata != NULL)
if (m_vbidata.count() != 0)
{
UINT32 vbiframe;
vbi_metadata_unpack(&m_metadata[m_fieldnum], &vbiframe, &m_vbidata[readhunk * VBI_PACKED_BYTES]);
@ -1079,13 +1079,30 @@ void laserdisc_device::read_track_data()
m_readresult = CHDERR_FILE_NOT_FOUND;
if (m_disc != NULL && !m_videosquelch)
{
m_readresult = chd_codec_config(m_disc, AV_CODEC_DECOMPRESS_CONFIG, &m_avconfig);
m_readresult = m_disc->codec_configure(CHD_CODEC_AVHUFF, AVHUFF_CODEC_DECOMPRESS_CONFIG, &m_avhuff_config);
if (m_readresult == CHDERR_NONE)
m_readresult = chd_read_async(m_disc, readhunk, NULL);
{
m_queued_hunknum = readhunk;
m_readresult = CHDERR_OPERATION_PENDING;
osd_work_item_queue(m_work_queue, read_async_static, this, WORK_ITEM_FLAG_AUTO_RELEASE);
}
}
}
//-------------------------------------------------
// read_async_static - work item callback for
// asynchronous reads
//-------------------------------------------------
void *laserdisc_device::read_async_static(void *param, int threadid)
{
laserdisc_device &ld = *reinterpret_cast<laserdisc_device *>(param);
ld.m_readresult = ld.m_disc->read_hunk(ld.m_queued_hunknum, NULL);
return NULL;
}
//-------------------------------------------------
// process_track_data - process data from a
// track after it has been read
@ -1095,27 +1112,28 @@ void laserdisc_device::process_track_data()
{
// wait for the async operation to complete
if (m_readresult == CHDERR_OPERATION_PENDING)
m_readresult = chd_async_complete(m_disc);
osd_work_queue_wait(m_work_queue, osd_ticks_per_second() * 10);
assert(m_readresult != CHDERR_OPERATION_PENDING);
// remove the video if we had an error
if (m_readresult != CHDERR_NONE)
m_avconfig.video.reset();
m_avhuff_config.video.reset();
// count the field as read if we are successful
if (m_avconfig.video.valid())
if (m_avhuff_config.video.valid())
{
m_frame[m_videoindex].m_numfields++;
player_overlay(m_avconfig.video);
player_overlay(m_avhuff_config.video);
}
// pass the audio to the callback
if (!m_audio_callback.isnull())
m_audio_callback(*this, m_samplerate, m_audiocursamples, m_avconfig.audio[0], m_avconfig.audio[1]);
m_audio_callback(*this, m_samplerate, m_audiocursamples, m_avhuff_config.audio[0], m_avhuff_config.audio[1]);
// shift audio data if we read it into the beginning of the buffer
if (m_audiocursamples != 0 && m_audiobufin != 0)
for (int chnum = 0; chnum < 2; chnum++)
if (m_avconfig.audio[chnum] == &m_audiobuffer[chnum][0])
if (m_avhuff_config.audio[chnum] == &m_audiobuffer[chnum][0])
{
// move data to the end
int samplesleft = m_audiobufsize - m_audiobufin;

View File

@ -43,6 +43,7 @@
#define __LASERDSC_H__
#include "vbiparse.h"
#include "avhuff.h"
//**************************************************************************
@ -315,6 +316,7 @@ private:
void vblank_state_changed(screen_device &screen, bool vblank_state);
frame_data &current_frame();
void read_track_data();
static void *read_async_static(void *param, int threadid);
void process_track_data();
void config_load(int config_type, xml_data_node *parentnode);
void config_save(int config_type, xml_data_node *parentnode);
@ -332,14 +334,18 @@ private:
// disc parameters
chd_file * m_disc; // handle to the disc itself
UINT8 * m_vbidata; // pointer to precomputed VBI data
dynamic_buffer m_vbidata; // pointer to precomputed VBI data
int m_width; // width of video
int m_height; // height of video
UINT32 m_fps_times_1million; // frame rate of video
int m_samplerate; // audio samplerate
chd_error m_readresult; // result of the most recent read
int m_readresult; // result of the most recent read
UINT32 m_chdtracks; // number of tracks in the CHD
av_codec_decompress_config m_avconfig; // decompression configuration
avhuff_decompress_config m_avhuff_config; // decompression configuration
// async operations
osd_work_queue * m_work_queue; // work queue
UINT32 m_queued_hunknum; // queued hunk
// core states
UINT8 m_audiosquelch; // audio squelch state: bit 0 = audio 1, bit 1 = audio 2

View File

@ -38,33 +38,21 @@ class open_chd
friend class simple_list<open_chd>;
public:
open_chd(const char *region, emu_file &file, chd_file &chdfile, emu_file *difffile = NULL, chd_file *diffchd = NULL)
open_chd(const char *region)
: m_next(NULL),
m_region(region),
m_origchd(&chdfile),
m_origfile(&file),
m_diffchd(diffchd),
m_difffile(difffile) { }
~open_chd()
{
if (m_diffchd != NULL) chd_close(m_diffchd);
global_free(m_difffile);
chd_close(m_origchd);
global_free(m_origfile);
}
m_region(region) { }
open_chd *next() const { return m_next; }
const char *region() const { return m_region; }
chd_file *chd() const { return (m_diffchd != NULL) ? m_diffchd : m_origchd; }
chd_file &chd() { return m_diffchd.opened() ? m_diffchd : m_origchd; }
chd_file &orig_chd() { return m_origchd; }
chd_file &diff_chd() { return m_diffchd; }
private:
open_chd * m_next; /* pointer to next in the list */
astring m_region; /* disk region we came from */
chd_file * m_origchd; /* handle to the original CHD */
emu_file * m_origfile; /* file handle to the original CHD file */
chd_file * m_diffchd; /* handle to the diff CHD */
emu_file * m_difffile; /* file handle to the diff CHD file */
chd_file m_origchd; /* handle to the original CHD */
chd_file m_diffchd; /* handle to the diff CHD */
};
@ -105,21 +93,15 @@ static void rom_exit(running_machine &machine);
HELPERS (also used by devimage.c)
***************************************************************************/
file_error common_process_file(emu_options &options, const char *location, const char *ext, const rom_entry *romp, emu_file **image_file)
file_error common_process_file(emu_options &options, const char *location, const char *ext, const rom_entry *romp, emu_file &image_file)
{
*image_file = global_alloc(emu_file(options.media_path(), OPEN_FLAG_READ));
file_error filerr;
if (location != NULL && strcmp(location, "") != 0)
filerr = (*image_file)->open(location, PATH_SEPARATOR, ROM_GETNAME(romp), ext);
filerr = image_file.open(location, PATH_SEPARATOR, ROM_GETNAME(romp), ext);
else
filerr = (*image_file)->open(ROM_GETNAME(romp), ext);
filerr = image_file.open(ROM_GETNAME(romp), ext);
if (filerr != FILERR_NONE)
{
global_free(*image_file);
*image_file = NULL;
}
return filerr;
}
@ -155,7 +137,7 @@ chd_file *get_disk_handle(running_machine &machine, const char *region)
{
for (open_chd *curdisk = machine.romload_data->chd_list.first(); curdisk != NULL; curdisk = curdisk->next())
if (strcmp(curdisk->region(), region) == 0)
return curdisk->chd();
return &curdisk->chd();
return NULL;
}
@ -165,9 +147,15 @@ chd_file *get_disk_handle(running_machine &machine, const char *region)
file associated with the given region
-------------------------------------------------*/
void set_disk_handle(running_machine &machine, const char *region, emu_file &file, chd_file &chdfile)
int set_disk_handle(running_machine &machine, const char *region, const char *fullpath)
{
machine.romload_data->chd_list.append(*global_alloc(open_chd(region, file, chdfile)));
open_chd *chd = global_alloc(open_chd(region));
chd_error err = chd->orig_chd().open(fullpath);
if (err == CHDERR_NONE)
machine.romload_data->chd_list.append(*chd);
else
global_free(chd);
return err;
}
@ -1010,16 +998,14 @@ static void process_rom_entries(rom_load_data *romdata, const char *regiontag, c
checksum
-------------------------------------------------*/
chd_error open_disk_image(emu_options &options, const game_driver *gamedrv, const rom_entry *romp, emu_file **image_file, chd_file **image_chd, const char *locationtag)
int open_disk_image(emu_options &options, const game_driver *gamedrv, const rom_entry *romp, chd_file &image_chd, const char *locationtag)
{
emu_file image_file(options.media_path(), OPEN_FLAG_READ);
const rom_entry *region, *rom;
const rom_source *source;
file_error filerr;
chd_error err;
*image_file = NULL;
*image_chd = NULL;
/* attempt to open the properly named file, scanning up through parent directories */
filerr = FILERR_NOT_FOUND;
for (int searchdrv = driver_list::find(*gamedrv); searchdrv != -1 && filerr != FILERR_NONE; searchdrv = driver_list::clone(searchdrv))
@ -1103,14 +1089,13 @@ chd_error open_disk_image(emu_options &options, const game_driver *gamedrv, cons
/* did the file open succeed? */
if (filerr == FILERR_NONE)
{
astring fullpath(image_file.fullpath());
image_file.close();
/* try to open the CHD */
err = chd_open_file(**image_file, CHD_OPEN_READ, NULL, image_chd);
err = image_chd.open(fullpath);
if (err == CHDERR_NONE)
return err;
/* close the file on failure */
global_free(*image_file);
*image_file = NULL;
}
else
err = CHDERR_FILE_NOT_FOUND;
@ -1141,18 +1126,16 @@ chd_error open_disk_image(emu_options &options, const game_driver *gamedrv, cons
/* did the file open succeed? */
if (filerr == FILERR_NONE)
{
astring fullpath(image_file.fullpath());
image_file.close();
/* try to open the CHD */
err = chd_open_file(**image_file, CHD_OPEN_READ, NULL, image_chd);
err = image_chd.open(fullpath);
if (err == CHDERR_NONE)
return err;
/* close the file on failure */
global_free(*image_file);
*image_file = NULL;
}
}
}
return err;
}
@ -1161,48 +1144,43 @@ chd_error open_disk_image(emu_options &options, const game_driver *gamedrv, cons
open_disk_diff - open a DISK diff file
-------------------------------------------------*/
static chd_error open_disk_diff(emu_options &options, const rom_entry *romp, chd_file *source, emu_file **diff_file, chd_file **diff_chd)
static chd_error open_disk_diff(emu_options &options, const rom_entry *romp, chd_file &source, chd_file &diff_chd)
{
astring fname(ROM_GETNAME(romp), ".dif");
chd_error err;
*diff_file = NULL;
*diff_chd = NULL;
/* try to open the diff */
LOG(("Opening differencing image file: %s\n", fname.cstr()));
*diff_file = global_alloc(emu_file(options.diff_directory(), OPEN_FLAG_READ | OPEN_FLAG_WRITE));
file_error filerr = (*diff_file)->open(fname);
if (filerr != FILERR_NONE)
emu_file diff_file(options.diff_directory(), OPEN_FLAG_READ | OPEN_FLAG_WRITE);
file_error filerr = diff_file.open(fname);
if (filerr == FILERR_NONE)
{
/* didn't work; try creating it instead */
LOG(("Creating differencing image: %s\n", fname.cstr()));
(*diff_file)->set_openflags(OPEN_FLAG_READ | OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS);
filerr = (*diff_file)->open(fname);
if (filerr != FILERR_NONE)
{
err = CHDERR_FILE_NOT_FOUND;
goto done;
}
astring fullpath(diff_file.fullpath());
diff_file.close();
LOG(("Opening differencing image file: %s\n", fullpath.cstr()));
return diff_chd.open(fullpath, true, &source);
}
/* didn't work; try creating it instead */
LOG(("Creating differencing image: %s\n", fname.cstr()));
diff_file.set_openflags(OPEN_FLAG_READ | OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS);
filerr = diff_file.open(fname);
if (filerr == FILERR_NONE)
{
astring fullpath(diff_file.fullpath());
diff_file.close();
/* create the CHD */
err = chd_create_file(**diff_file, 0, 0, CHDCOMPRESSION_NONE, source);
LOG(("Creating differencing image file: %s\n", fullpath.cstr()));
chd_codec_type compression[4] = { CHD_CODEC_NONE };
chd_error err = diff_chd.create(fullpath, source.logical_bytes(), source.hunk_bytes(), compression, source);
if (err != CHDERR_NONE)
goto done;
return err;
return diff_chd.clone_all_metadata(source);
}
LOG(("Opening differencing image file: %s\n", fname.cstr()));
err = chd_open_file(**diff_file, CHD_OPEN_READWRITE, source, diff_chd);
if (err != CHDERR_NONE)
goto done;
done:
if ((err != CHDERR_NONE) && (*diff_file != NULL))
{
global_free(*diff_file);
*diff_file = NULL;
}
return err;
return CHDERR_FILE_NOT_FOUND;
}
@ -1219,24 +1197,23 @@ static void process_disk_entries(rom_load_data *romdata, const char *regiontag,
/* handle files */
if (ROMENTRY_ISFILE(romp))
{
open_chd *chd = global_alloc(open_chd(regiontag));
hash_collection hashes(ROM_GETHASHDATA(romp));
chd_header header;
chd_error err;
/* make the filename of the source */
astring filename(ROM_GETNAME(romp), ".chd");
/* first open the source drive */
chd_file *origchd;
emu_file *origfile;
LOG(("Opening disk image: %s\n", filename.cstr()));
err = open_disk_image(romdata->machine().options(), &romdata->machine().system(), romp, &origfile, &origchd, locationtag);
err = chd_error(open_disk_image(romdata->machine().options(), &romdata->machine().system(), romp, chd->orig_chd(), locationtag));
if (err != CHDERR_NONE)
{
if (err == CHDERR_FILE_NOT_FOUND)
romdata->errorstring.catprintf("%s NOT FOUND\n", filename.cstr());
else
romdata->errorstring.catprintf("%s CHD ERROR: %s\n", filename.cstr(), chd_error_string(err));
romdata->errorstring.catprintf("%s CHD ERROR: %s\n", filename.cstr(), chd_file::error_string(err));
/* if this is NO_DUMP, keep going, though the system may not be able to handle it */
if (hashes.flag(hash_collection::FLAG_NO_DUMP))
@ -1245,13 +1222,13 @@ static void process_disk_entries(rom_load_data *romdata, const char *regiontag,
romdata->warnings++;
else
romdata->errors++;
global_free(chd);
continue;
}
/* get the header and extract the SHA1 */
header = *chd_get_header(origchd);
hash_collection acthashes;
acthashes.add_from_buffer(hash_collection::HASH_SHA1, header.sha1, sizeof(header.sha1));
acthashes.add_from_buffer(hash_collection::HASH_SHA1, chd->orig_chd().sha1().m_raw, sizeof(chd->orig_chd().sha1().m_raw));
/* verify the hash */
if (hashes != acthashes)
@ -1267,23 +1244,22 @@ static void process_disk_entries(rom_load_data *romdata, const char *regiontag,
}
/* if not read-only, make the diff file */
chd_file *diffchd = NULL;
emu_file *difffile = NULL;
if (!DISK_ISREADONLY(romp))
{
/* try to open or create the diff */
err = open_disk_diff(romdata->machine().options(), romp, origchd, &difffile, &diffchd);
err = open_disk_diff(romdata->machine().options(), romp, chd->orig_chd(), chd->diff_chd());
if (err != CHDERR_NONE)
{
romdata->errorstring.catprintf("%s DIFF CHD ERROR: %s\n", filename.cstr(), chd_error_string(err));
romdata->errorstring.catprintf("%s DIFF CHD ERROR: %s\n", filename.cstr(), chd_file::error_string(err));
romdata->errors++;
global_free(chd);
continue;
}
}
/* we're okay, add to the list of disks */
LOG(("Assigning to handle %d\n", DISK_GETINDEX(romp)));
romdata->machine().romload_data->chd_list.append(*global_alloc(open_chd(regiontag, *origfile, *origchd, difffile, diffchd)));
romdata->machine().romload_data->chd_list.append(*chd);
}
}
}

View File

@ -121,6 +121,7 @@ enum
class machine_config;
class emu_options;
class chd_file;
typedef device_t rom_source;
@ -315,13 +316,13 @@ astring &rom_region_name(astring &result, const game_driver *drv, const rom_sour
/* ----- disk handling ----- */
/* open a disk image, searching up the parent and loading by checksum */
chd_error open_disk_image(emu_options &options, const game_driver *gamedrv, const rom_entry *romp, emu_file **image_file, chd_file **image_chd,const char *locationtag);
int open_disk_image(emu_options &options, const game_driver *gamedrv, const rom_entry *romp, chd_file &image_chd, const char *locationtag);
/* return a pointer to the CHD file associated with the given region */
chd_file *get_disk_handle(running_machine &machine, const char *region);
/* set a pointer to the CHD file associated with the given region */
void set_disk_handle(running_machine &machine, const char *region, emu_file &file, chd_file &chdfile);
int set_disk_handle(running_machine &machine, const char *region, const char *fullpath);
void load_software_part_region(device_t *device, char *swlist, char *swname, rom_entry *start_region);

View File

@ -166,8 +166,6 @@ chd_file *ldplayer_state::get_disc()
// iterate while we get new objects
const osd_directory_entry *dir;
emu_file *image_file = NULL;
chd_file *image_chd = NULL;
while ((dir = path.next()) != NULL)
{
int length = strlen(dir->name);
@ -180,15 +178,17 @@ chd_file *ldplayer_state::get_disc()
tolower(dir->name[length - 1]) == 'd')
{
// open the file itself via our search path
image_file = auto_alloc(machine(), emu_file(machine().options().media_path(), OPEN_FLAG_READ));
file_error filerr = image_file->open(dir->name);
emu_file image_file(machine().options().media_path(), OPEN_FLAG_READ);
file_error filerr = image_file.open(dir->name);
if (filerr == FILERR_NONE)
{
astring fullpath(image_file->fullpath();
image_file.close();
// try to open the CHD
chd_error chderr = chd_open_file(*image_file, CHD_OPEN_READ, NULL, &image_chd);
if (chderr == CHDERR_NONE)
if (set_disk_handle(machine(), "laserdisc", fullpath) == CHDERR_NONE)
{
set_disk_handle(machine(), "laserdisc", *image_file, *image_chd);
m_filename.cpy(dir->name);
break;
}

View File

@ -32,16 +32,19 @@ OBJDIRS += \
UTILOBJS = \
$(LIBOBJ)/util/astring.o \
$(LIBOBJ)/util/avcomp.o \
$(LIBOBJ)/util/avhuff.o \
$(LIBOBJ)/util/aviio.o \
$(LIBOBJ)/util/bitmap.o \
$(LIBOBJ)/util/cdrom.o \
$(LIBOBJ)/util/chd.o \
$(LIBOBJ)/util/chdcd.o \
$(LIBOBJ)/util/chdcodec.o \
$(LIBOBJ)/util/corefile.o \
$(LIBOBJ)/util/corestr.o \
$(LIBOBJ)/util/coreutil.o \
$(LIBOBJ)/util/flac.o \
$(LIBOBJ)/util/harddisk.o \
$(LIBOBJ)/util/hashing.o \
$(LIBOBJ)/util/huffman.o \
$(LIBOBJ)/util/jedparse.o \
$(LIBOBJ)/util/md5.o \
@ -73,7 +76,7 @@ EXPATOBJS = \
$(OBJ)/libexpat.a: $(EXPATOBJS)
$(LIBOBJ)/expat/%.o: $(LIBSRC)/explat/%.c | $(OSPREBUILD)
$(LIBOBJ)/expat/%.o: $(LIBSRC)/expat/%.c | $(OSPREBUILD)
@echo Compiling $<...
$(CC) $(CDEFS) $(CCOMFLAGS) $(CONLYFLAGS) -c $< -o $@
@ -273,6 +276,7 @@ $(LIBOBJ)/libjpeg/%.o: $(LIBSRC)/libjpeg/%.c | $(OSPREBUILD)
$(CC) $(CDEFS) $(CCOMFLAGS) $(CONLYFLAGS) -I$(LIBSRC)/libjpeg -c $< -o $@
#-------------------------------------------------
# libflac library objects
#-------------------------------------------------
@ -303,23 +307,12 @@ $(LIBOBJ)/libflac/%.o: $(LIBSRC)/libflac/libflac/%.c | $(OSPREBUILD)
$(CC) $(CDEFS) $(FLACOPTS) $(CONLYFLAGS) -I$(LIBSRC)/libflac/include -c $< -o $@
# LIBFLACPPOBJS = \
# $(LIBOBJ)/libflacpp/metadata.o \
# $(LIBOBJ)/libflacpp/stream_decoder.o \
# $(LIBOBJ)/libflacpp/stream_encoder.o
# $(OBJ)/libflac++.a: $(LIBFLACPPOBJS)
# $(LIBOBJ)/libflacpp/%.o: $(LIBSRC)/libflac/libflac++/%.cpp | $(OSPREBUILD)
# @echo Compiling $<...
# $(CC) $(CDEFS) $(FLACOPTS) $(CPPONLYFLAGS) -I$(LIBSRC)/libflac/include -c $< -o $@
#-------------------------------------------------
# lib7z library objects
#-------------------------------------------------
7ZOPTS=-D_7ZIP_PPMD_SUPPPORT
7ZOPTS=-D_7ZIP_PPMD_SUPPPORT -D_7ZIP_ST
LIB7ZOBJS = \
$(LIBOBJ)/lib7z/7zBuf.o \
@ -331,6 +324,9 @@ LIB7ZOBJS = \
$(LIBOBJ)/lib7z/CpuArch.o \
$(LIBOBJ)/lib7z/LzmaDec.o \
$(LIBOBJ)/lib7z/Lzma2Dec.o \
$(LIBOBJ)/lib7z/LzmaEnc.o \
$(LIBOBJ)/lib7z/Lzma2Enc.o \
$(LIBOBJ)/lib7z/LzFind.o \
$(LIBOBJ)/lib7z/Bra.o \
$(LIBOBJ)/lib7z/Bra86.o \
$(LIBOBJ)/lib7z/Bcj2.o \
@ -343,6 +339,3 @@ $(OBJ)/lib7z.a: $(LIB7ZOBJS)
$(LIBOBJ)/lib7z/%.o: $(LIBSRC)/lib7z/%.c | $(OSPREBUILD)
@echo Compiling $<...
$(CC) $(CDEFS) $(7ZOPTS) $(CONLYFLAGS) -I$(LIBSRC)/lib7z/ -c $< -o $@

View File

@ -967,6 +967,27 @@ SRes LzmaDec_Allocate(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAll
return SZ_OK;
}
// why isn't there an interface to pass in the properties directly????
SRes LzmaDec_Allocate_MAME(CLzmaDec *p, const CLzmaProps *propNew, ISzAlloc *alloc)
{
SizeT dicBufSize;
RINOK(LzmaDec_AllocateProbs2(p, propNew, alloc));
dicBufSize = propNew->dicSize;
if (p->dic == 0 || dicBufSize != p->dicBufSize)
{
LzmaDec_FreeDict(p, alloc);
p->dic = (Byte *)alloc->Alloc(alloc, dicBufSize);
if (p->dic == 0)
{
LzmaDec_FreeProbs(p, alloc);
return SZ_ERROR_MEM;
}
}
p->dicBufSize = dicBufSize;
p->prop = *propNew;
return SZ_OK;
}
SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen,
const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode,
ELzmaStatus *status, ISzAlloc *alloc)

View File

@ -2,7 +2,7 @@
2010-03-12 : Igor Pavlov : Public domain
This code is based on PPMd var.H (2001): Dmitry Shkarin : Public domain */
#include <memory.h>
#include <string.h>
#include "Ppmd7.h"

View File

@ -52,7 +52,7 @@
// TYPE DEFINITIONS
//**************************************************************************
// derived class for C++
// basic allocated string class
class astring
{
public:

View File

@ -1,898 +0,0 @@
/***************************************************************************
avcomp.c
Audio/video compression and decompression helpers.
****************************************************************************
Copyright Aaron Giles
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name 'MAME' nor the names of its contributors may be
used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
****************************************************************************
Each frame is compressed as a unit. The raw data is of the form:
(all multibyte values are stored in big-endian format)
+00 = 'chav' (4 bytes) - fixed header data to identify the format
+04 = metasize (1 byte) - size of metadata in bytes (max=255 bytes)
+05 = channels (1 byte) - number of audio channels
+06 = samples (2 bytes) - number of samples per audio stream
+08 = width (2 bytes) - width of video data
+0A = height (2 bytes) - height of video data
+0C = <metadata> - as raw bytes
<audio stream 0> - as signed 16-bit samples
<audio stream 1> - as signed 16-bit samples
...
<video data> - as a raw array of 8-bit YUY data in (Cb,Y,Cr,Y) order
When compressed, the data is stored as follows:
(all multibyte values are stored in big-endian format)
+00 = metasize (1 byte) - size of metadata in bytes
+01 = channels (1 byte) - number of audio channels
+02 = samples (2 bytes) - number of samples per audio stream
+04 = width (2 bytes) - width of video data
+06 = height (2 bytes) - height of video data
+08 = audio huffman size (2 bytes) - size of audio huffman tables
+0A = str0size (2 bytes) - compressed size of stream 0
+0C = str1size (2 bytes) - compressed size of stream 1
...
<metadata> - as raw data
<audio huffman table> - Huffman table for audio decoding
<audio stream 0 data> - Huffman-compressed deltas
<audio stream 1 data> - Huffman-compressed deltas
<...>
<video huffman tables> - Huffman tables for video decoding
<video data> - compressed data
****************************************************************************
Attempted techniques that have not been worthwhile:
* Attempted to use integer DCTs from the IJG code; even the "slow"
variants produce a lot of error and thus kill our compression ratio,
since our compression is based on error not bitrate.
* Tried various other predictors for the lossless video encoding, but
none tended to give any significant gain over predicting the
previous pixel.
***************************************************************************/
#include "avcomp.h"
#include "huffman.h"
#include "chd.h"
#include <math.h>
#include <stdlib.h>
#include <new>
/***************************************************************************
CONSTANTS
***************************************************************************/
#define MAX_CHANNELS 4
/***************************************************************************
TYPE DEFINITIONS
***************************************************************************/
struct avcomp_state
{
avcomp_state()
: maxwidth(0),
maxheight(0),
maxchannels(0),
audiodata(NULL),
ycontext(NULL),
cbcontext(NULL),
crcontext(NULL),
audiohicontext(NULL),
audiolocontext(NULL) { }
/* video parameters */
UINT32 maxwidth, maxheight;
/* audio parameters */
UINT32 maxchannels;
/* intermediate data */
UINT8 * audiodata;
/* huffman contexts */
huffman_context * ycontext;
huffman_context * cbcontext;
huffman_context * crcontext;
huffman_context * audiohicontext;
huffman_context * audiolocontext;
/* configuration data */
av_codec_compress_config compress;
av_codec_decompress_config decompress;
};
/***************************************************************************
PROTOTYPES
***************************************************************************/
/* encoding helpers */
static avcomp_error encode_audio(avcomp_state *state, int channels, int samples, const UINT8 **source, int sourcexor, UINT8 *dest, UINT8 *sizes);
static avcomp_error encode_video(avcomp_state *state, int width, int height, const UINT8 *source, UINT32 sstride, UINT32 sxor, UINT8 *dest, UINT32 *complength);
static avcomp_error encode_video_lossless(avcomp_state *state, int width, int height, const UINT8 *source, UINT32 sstride, UINT32 sxor, UINT8 *dest, UINT32 *complength);
/* decoding helpers */
static avcomp_error decode_audio(avcomp_state *state, int channels, int samples, const UINT8 *source, UINT8 **dest, UINT32 dxor, const UINT8 *sizes);
static avcomp_error decode_video(avcomp_state *state, int width, int height, const UINT8 *source, UINT32 complength, UINT8 *dest, UINT32 dstride, UINT32 dxor);
static avcomp_error decode_video_lossless(avcomp_state *state, int width, int height, const UINT8 *source, UINT32 complength, UINT8 *dest, UINT32 deststride, UINT32 destxor);
/***************************************************************************
IMPLEMENTATION
***************************************************************************/
/*-------------------------------------------------
avcomp_init - allocate and initialize a
new state block for compression or
decompression
-------------------------------------------------*/
avcomp_state *avcomp_init(UINT32 maxwidth, UINT32 maxheight, UINT32 maxchannels)
{
huffman_error hufferr;
avcomp_state *state;
/* error if out of range */
if (maxchannels > MAX_CHANNELS)
return NULL;
/* allocate memory for state block */
state = new(std::nothrow) avcomp_state;
if (state == NULL)
return NULL;
/* compute the core info */
state->maxwidth = maxwidth;
state->maxheight = maxheight;
state->maxchannels = maxchannels;
/* now allocate data buffers */
state->audiodata = new(std::nothrow) UINT8[65536 * state->maxchannels * 2];
if (state->audiodata == NULL)
goto cleanup;
/* create huffman contexts */
hufferr = huffman_create_context(&state->ycontext, 16);
if (hufferr != HUFFERR_NONE)
goto cleanup;
hufferr = huffman_create_context(&state->cbcontext, 16);
if (hufferr != HUFFERR_NONE)
goto cleanup;
hufferr = huffman_create_context(&state->crcontext, 16);
if (hufferr != HUFFERR_NONE)
goto cleanup;
hufferr = huffman_create_context(&state->audiohicontext, 16);
if (hufferr != HUFFERR_NONE)
goto cleanup;
hufferr = huffman_create_context(&state->audiolocontext, 16);
if (hufferr != HUFFERR_NONE)
goto cleanup;
return state;
cleanup:
avcomp_free(state);
return NULL;
}
/*-------------------------------------------------
avcomp_free - free a state block
-------------------------------------------------*/
void avcomp_free(avcomp_state *state)
{
/* free the data buffers */
delete[] state->audiodata;
/* free the contexts */
if (state->ycontext != NULL)
huffman_free_context(state->ycontext);
if (state->cbcontext != NULL)
huffman_free_context(state->cbcontext);
if (state->crcontext != NULL)
huffman_free_context(state->crcontext);
if (state->audiohicontext != NULL)
huffman_free_context(state->audiohicontext);
if (state->audiolocontext != NULL)
huffman_free_context(state->audiolocontext);
delete state;
}
/*-------------------------------------------------
avcomp_config_compress - configure compression
parameters
-------------------------------------------------*/
void avcomp_config_compress(avcomp_state *state, av_codec_compress_config *config)
{
state->compress.video.wrap(config->video, config->video.cliprect());
state->compress.channels = config->channels;
state->compress.samples = config->samples;
memcpy(state->compress.audio, config->audio, sizeof(state->compress.audio));
state->compress.metalength = config->metalength;
state->compress.metadata = config->metadata;
}
/*-------------------------------------------------
avcomp_config_decompress - configure
decompression parameters
-------------------------------------------------*/
void avcomp_config_decompress(avcomp_state *state, av_codec_decompress_config *config)
{
state->decompress.video.wrap(config->video, config->video.cliprect());
state->decompress.maxsamples = config->maxsamples;
state->decompress.actsamples = config->actsamples;
memcpy(state->decompress.audio, config->audio, sizeof(state->decompress.audio));
state->decompress.maxmetalength = config->maxmetalength;
state->decompress.actmetalength = config->actmetalength;
state->decompress.metadata = config->metadata;
}
/***************************************************************************
ENCODING/DECODING FRONTENDS
***************************************************************************/
/*-------------------------------------------------
avcomp_encode_data - encode a block of data
into a compressed data stream
-------------------------------------------------*/
avcomp_error avcomp_encode_data(avcomp_state *state, const UINT8 *source, UINT8 *dest, UINT32 *complength)
{
const UINT8 *metastart, *videostart, *audiostart[MAX_CHANNELS];
UINT32 metasize, channels, samples, width, height;
UINT32 audioxor, videoxor, videostride;
avcomp_error err;
UINT32 dstoffs;
int chnum;
/* extract data from source if present */
if (source != NULL)
{
/* validate the header */
if (source[0] != 'c' || source[1] != 'h' || source[2] != 'a' || source[3] != 'v')
return AVCERR_INVALID_DATA;
/* extract info from the header */
metasize = source[4];
channels = source[5];
samples = (source[6] << 8) + source[7];
width = (source[8] << 8) + source[9];
height = (source[10] << 8) + source[11];
/* determine the start of each piece of data */
source += 12;
metastart = source;
source += metasize;
for (chnum = 0; chnum < channels; chnum++)
{
audiostart[chnum] = source;
source += 2 * samples;
}
videostart = source;
/* data is assumed to be big-endian already */
audioxor = videoxor = 0;
videostride = 2 * width;
}
/* otherwise, extract from the state */
else
{
UINT16 betest = 0;
/* extract metadata information */
metastart = state->compress.metadata;
metasize = state->compress.metalength;
if ((metastart == NULL && metasize != 0) || (metastart != NULL && metasize == 0))
return AVCERR_INVALID_CONFIGURATION;
/* extract audio information */
channels = state->compress.channels;
samples = state->compress.samples;
for (chnum = 0; chnum < channels; chnum++)
audiostart[chnum] = (const UINT8 *)state->compress.audio[chnum];
/* extract video information */
videostart = NULL;
videostride = width = height = 0;
if (state->compress.video.valid())
{
videostart = reinterpret_cast<const UINT8 *>(&state->compress.video.pix(0));
videostride = state->compress.video.rowpixels() * 2;
width = state->compress.video.width();
height = state->compress.video.height();
}
/* data is assumed to be native-endian */
*(UINT8 *)&betest = 1;
audioxor = videoxor = (betest == 1) ? 1 : 0;
}
/* validate the info from the header */
if (width > state->maxwidth || height > state->maxheight)
return AVCERR_VIDEO_TOO_LARGE;
if (channels > state->maxchannels)
return AVCERR_AUDIO_TOO_LARGE;
/* write the basics to the new header */
dest[0] = metasize;
dest[1] = channels;
dest[2] = samples >> 8;
dest[3] = samples;
dest[4] = width >> 8;
dest[5] = width;
dest[6] = height >> 8;
dest[7] = height;
/* starting offsets */
dstoffs = 10 + 2 * channels;
/* copy the metadata first */
if (metasize > 0)
{
memcpy(dest + dstoffs, metastart, metasize);
dstoffs += metasize;
}
/* encode the audio channels */
if (channels > 0)
{
/* encode the audio */
err = encode_audio(state, channels, samples, audiostart, audioxor, dest + dstoffs, &dest[8]);
if (err != AVCERR_NONE)
return err;
/* advance the pointers past the data */
dstoffs += (dest[8] << 8) + dest[9];
for (chnum = 0; chnum < channels; chnum++)
dstoffs += (dest[10 + 2 * chnum] << 8) + dest[11 + 2 * chnum];
}
/* encode the video data */
if (width > 0 && height > 0)
{
UINT32 vidlength = 0;
/* encode the video */
err = encode_video(state, width, height, videostart, videostride, videoxor, dest + dstoffs, &vidlength);
if (err != AVCERR_NONE)
return err;
/* advance the pointers past the data */
dstoffs += vidlength;
}
/* set the total compression */
*complength = dstoffs;
return AVCERR_NONE;
}
/*-------------------------------------------------
avcomp_decode_data - decode both
audio and video from a raw data stream
-------------------------------------------------*/
avcomp_error avcomp_decode_data(avcomp_state *state, const UINT8 *source, UINT32 complength, UINT8 *dest)
{
UINT8 *metastart, *videostart, *audiostart[MAX_CHANNELS];
UINT32 metasize, channels, samples, width, height;
UINT32 audioxor, videoxor, videostride;
UINT32 srcoffs, totalsize;
avcomp_error err;
int chnum;
/* extract info from the header */
if (complength < 8)
return AVCERR_INVALID_DATA;
metasize = source[0];
channels = source[1];
samples = (source[2] << 8) + source[3];
width = (source[4] << 8) + source[5];
height = (source[6] << 8) + source[7];
/* validate the info from the header */
if (width > state->maxwidth || height > state->maxheight)
return AVCERR_VIDEO_TOO_LARGE;
if (channels > state->maxchannels)
return AVCERR_AUDIO_TOO_LARGE;
/* validate that the sizes make sense */
if (complength < 10 + 2 * channels)
return AVCERR_INVALID_DATA;
totalsize = 10 + 2 * channels;
totalsize += (source[8] << 8) | source[9];
for (chnum = 0; chnum < channels; chnum++)
totalsize += (source[10 + 2 * chnum] << 8) | source[11 + 2 * chnum];
if (totalsize >= complength)
return AVCERR_INVALID_DATA;
/* starting offsets */
srcoffs = 10 + 2 * channels;
/* if we are decoding raw, set up the output parameters */
if (dest != NULL)
{
/* create a header */
dest[0] = 'c';
dest[1] = 'h';
dest[2] = 'a';
dest[3] = 'v';
dest[4] = metasize;
dest[5] = channels;
dest[6] = samples >> 8;
dest[7] = samples;
dest[8] = width >> 8;
dest[9] = width;
dest[10] = height >> 8;
dest[11] = height;
/* determine the start of each piece of data */
dest += 12;
metastart = dest;
dest += metasize;
for (chnum = 0; chnum < channels; chnum++)
{
audiostart[chnum] = dest;
dest += 2 * samples;
}
videostart = dest;
/* data is assumed to be big-endian already */
audioxor = videoxor = 0;
videostride = 2 * width;
}
/* otherwise, extract from the state */
else
{
UINT16 betest = 0;
/* determine the start of each piece of data */
metastart = state->decompress.metadata;
for (chnum = 0; chnum < channels; chnum++)
audiostart[chnum] = (UINT8 *)state->decompress.audio[chnum];
videostart = (state->decompress.video.valid()) ? reinterpret_cast<UINT8 *>(&state->decompress.video.pix(0)) : NULL;
videostride = (state->decompress.video.valid()) ? state->decompress.video.rowpixels() * 2 : 0;
/* data is assumed to be native-endian */
*(UINT8 *)&betest = 1;
audioxor = videoxor = (betest == 1) ? 1 : 0;
/* verify against sizes */
if (state->decompress.video.valid() && (state->decompress.video.width() < width || state->decompress.video.height() < height))
return AVCERR_VIDEO_TOO_LARGE;
for (chnum = 0; chnum < channels; chnum++)
if (state->decompress.audio[chnum] != NULL && state->decompress.maxsamples < samples)
return AVCERR_AUDIO_TOO_LARGE;
if (state->decompress.metadata != NULL && state->decompress.maxmetalength < metasize)
return AVCERR_METADATA_TOO_LARGE;
/* set the output values */
if (state->decompress.actsamples != NULL)
*state->decompress.actsamples = samples;
if (state->decompress.actmetalength != NULL)
*state->decompress.actmetalength = metasize;
}
/* copy the metadata first */
if (metasize > 0)
{
if (metastart != NULL)
memcpy(metastart, source + srcoffs, metasize);
srcoffs += metasize;
}
/* decode the audio channels */
if (channels > 0)
{
/* decode the audio */
err = decode_audio(state, channels, samples, source + srcoffs, audiostart, audioxor, &source[8]);
if (err != AVCERR_NONE)
return err;
/* advance the pointers past the data */
srcoffs += (source[8] << 8) + source[9];
for (chnum = 0; chnum < channels; chnum++)
srcoffs += (source[10 + 2 * chnum] << 8) + source[11 + 2 * chnum];
}
/* decode the video data */
if (width > 0 && height > 0 && videostart != NULL)
{
/* decode the video */
err = decode_video(state, width, height, source + srcoffs, complength - srcoffs, videostart, videostride, videoxor);
if (err != AVCERR_NONE)
return err;
}
return AVCERR_NONE;
}
/***************************************************************************
ENCODING HELPERS
***************************************************************************/
/*-------------------------------------------------
encode_audio - encode raw audio data
to the destination
-------------------------------------------------*/
static avcomp_error encode_audio(avcomp_state *state, int channels, int samples, const UINT8 **source, int sourcexor, UINT8 *dest, UINT8 *sizes)
{
UINT32 size, huffsize, totalsize;
huffman_context *contexts[2];
huffman_error hufferr;
UINT8 *output = dest;
int chnum, sampnum;
UINT8 *deltabuf;
/* iterate over channels to compute deltas */
deltabuf = state->audiodata;
for (chnum = 0; chnum < channels; chnum++)
{
const UINT8 *srcdata = source[chnum];
INT16 prevsample = 0;
/* extract audio data into hi and lo deltas stored in big-endian order */
for (sampnum = 0; sampnum < samples; sampnum++)
{
INT16 newsample = (srcdata[0 ^ sourcexor] << 8) | srcdata[1 ^ sourcexor];
INT16 delta = newsample - prevsample;
prevsample = newsample;
*deltabuf++ = delta >> 8;
*deltabuf++ = delta;
srcdata += 2;
}
}
/* compute the trees */
contexts[0] = state->audiohicontext;
contexts[1] = state->audiolocontext;
hufferr = huffman_compute_tree_interleaved(2, contexts, state->audiodata, samples * 2, channels, samples * 2, 0);
if (hufferr != HUFFERR_NONE)
return AVCERR_COMPRESSION_ERROR;
/* export them to the output */
hufferr = huffman_export_tree(state->audiohicontext, output, 256, &size);
if (hufferr != HUFFERR_NONE)
return AVCERR_COMPRESSION_ERROR;
output += size;
hufferr = huffman_export_tree(state->audiolocontext, output, 256, &size);
if (hufferr != HUFFERR_NONE)
return AVCERR_COMPRESSION_ERROR;
output += size;
/* note the size of the two trees */
huffsize = output - dest;
sizes[0] = huffsize >> 8;
sizes[1] = huffsize;
/* iterate over channels */
totalsize = huffsize;
for (chnum = 0; chnum < channels; chnum++)
{
const UINT8 *input = state->audiodata + chnum * samples * 2;
/* encode the data */
hufferr = huffman_encode_data_interleaved(2, contexts, input, samples * 2, 1, 0, 0, output, samples * 2, &size);
if (hufferr != HUFFERR_NONE)
return AVCERR_COMPRESSION_ERROR;
output += size;
/* store the size of this stream */
totalsize += size;
if (totalsize >= channels * samples * 2)
break;
sizes[chnum * 2 + 2] = size >> 8;
sizes[chnum * 2 + 3] = size;
}
/* if we ran out of room, throw it all away and just store raw */
if (chnum < channels)
{
memcpy(dest, state->audiodata, channels * samples * 2);
size = samples * 2;
sizes[0] = sizes[1] = 0;
for (chnum = 0; chnum < channels; chnum++)
{
sizes[chnum * 2 + 2] = size >> 8;
sizes[chnum * 2 + 3] = size;
}
}
return AVCERR_NONE;
}
/*-------------------------------------------------
encode_video - encode raw video data
to the destination
-------------------------------------------------*/
static avcomp_error encode_video(avcomp_state *state, int width, int height, const UINT8 *source, UINT32 sstride, UINT32 sxor, UINT8 *dest, UINT32 *complength)
{
/* only lossless supported at this time */
return encode_video_lossless(state, width, height, source, sstride, sxor, dest, complength);
}
/*-------------------------------------------------
encode_video_lossless - do a lossless video
encoding using deltas and huffman encoding
-------------------------------------------------*/
static avcomp_error encode_video_lossless(avcomp_state *state, int width, int height, const UINT8 *source, UINT32 sstride, UINT32 sxor, UINT8 *dest, UINT32 *complength)
{
UINT32 srcbytes = width * height * 2;
huffman_context *contexts[4];
huffman_error hufferr;
UINT32 outbytes;
UINT8 *output;
/* set up the output; first byte is 0x80 to indicate lossless encoding */
output = dest;
*output++ = 0x80;
/* now encode to the destination using two trees, one for the Y and one for the Cr/Cb */
contexts[0] = state->ycontext;
contexts[1] = state->cbcontext;
contexts[2] = state->ycontext;
contexts[3] = state->crcontext;
/* compute the histograms for the data */
hufferr = huffman_deltarle_compute_tree_interleaved(4, contexts, source, width * 2, height, sstride, sxor);
if (hufferr != HUFFERR_NONE)
return AVCERR_COMPRESSION_ERROR;
/* export the trees to the data stream */
hufferr = huffman_deltarle_export_tree(state->ycontext, output, 256, &outbytes);
if (hufferr != HUFFERR_NONE)
return AVCERR_COMPRESSION_ERROR;
output += outbytes;
hufferr = huffman_deltarle_export_tree(state->cbcontext, output, 256, &outbytes);
if (hufferr != HUFFERR_NONE)
return AVCERR_COMPRESSION_ERROR;
output += outbytes;
hufferr = huffman_deltarle_export_tree(state->crcontext, output, 256, &outbytes);
if (hufferr != HUFFERR_NONE)
return AVCERR_COMPRESSION_ERROR;
output += outbytes;
/* encode the data using the trees */
hufferr = huffman_deltarle_encode_data_interleaved(4, contexts, source, width * 2, height, sstride, sxor, output, srcbytes, &outbytes);
if (hufferr != HUFFERR_NONE)
return AVCERR_COMPRESSION_ERROR;
output += outbytes;
/* set the final length */
*complength = output - dest;
return AVCERR_NONE;
}
/***************************************************************************
DECODING HELPERS
***************************************************************************/
/*-------------------------------------------------
decode_audio - decode audio from a
compressed data stream
-------------------------------------------------*/
static avcomp_error decode_audio(avcomp_state *state, int channels, int samples, const UINT8 *source, UINT8 **dest, UINT32 dxor, const UINT8 *sizes)
{
huffman_context *contexts[2];
UINT32 actsize, huffsize;
huffman_error hufferr;
int chnum, sampnum;
UINT16 size;
/* if no huffman length, just copy the data */
size = (sizes[0] << 8) | sizes[1];
if (size == 0)
{
/* loop over channels */
for (chnum = 0; chnum < channels; chnum++)
{
UINT8 *curdest = dest[chnum];
/* extract the size of this channel */
size = (sizes[chnum * 2 + 2] << 8) | sizes[chnum * 2 + 3];
/* extract data from the deltas */
if (dest[chnum] != NULL)
{
INT16 prevsample = 0;
for (sampnum = 0; sampnum < samples; sampnum++)
{
INT16 delta = (source[0] << 8) | source[1];
INT16 newsample = prevsample + delta;
prevsample = newsample;
curdest[0 ^ dxor] = newsample >> 8;
curdest[1 ^ dxor] = newsample;
source += 2;
curdest += 2;
}
}
else
source += size;
}
return AVCERR_NONE;
}
/* extract the huffman trees */
hufferr = huffman_import_tree(state->audiohicontext, source, size, &actsize);
if (hufferr != HUFFERR_NONE)
return AVCERR_INVALID_DATA;
source += actsize;
huffsize = actsize;
hufferr = huffman_import_tree(state->audiolocontext, source, size, &actsize);
if (hufferr != HUFFERR_NONE)
return AVCERR_INVALID_DATA;
source += actsize;
huffsize += actsize;
if (huffsize != size)
return AVCERR_INVALID_DATA;
/* set up the contexts */
contexts[0] = state->audiohicontext;
contexts[1] = state->audiolocontext;
/* now loop over channels and decode their data */
for (chnum = 0; chnum < channels; chnum++)
{
/* extract the size of this channel */
size = (sizes[chnum * 2 + 2] << 8) | sizes[chnum * 2 + 3];
/* decode the data */
if (dest[chnum] != NULL)
{
UINT8 *deltabuf = state->audiodata + chnum * samples * 2;
hufferr = huffman_decode_data_interleaved(2, contexts, source, size, deltabuf, samples * 2, 1, 0, 0, &actsize);
if (hufferr != HUFFERR_NONE || actsize != size)
return AVCERR_INVALID_DATA;
}
/* advance */
source += size;
}
/* reassemble audio from the deltas */
for (chnum = 0; chnum < channels; chnum++)
if (dest[chnum] != NULL)
{
UINT8 *deltabuf = state->audiodata + chnum * samples * 2;
UINT8 *curdest = dest[chnum];
INT16 prevsample = 0;
for (sampnum = 0; sampnum < samples; sampnum++)
{
INT16 delta = (deltabuf[0] << 8) | deltabuf[1];
INT16 newsample = prevsample + delta;
prevsample = newsample;
curdest[0 ^ dxor] = newsample >> 8;
curdest[1 ^ dxor] = newsample;
deltabuf += 2;
curdest += 2;
}
}
return AVCERR_NONE;
}
/*-------------------------------------------------
decode_video - decode video from a
compressed data stream
-------------------------------------------------*/
static avcomp_error decode_video(avcomp_state *state, int width, int height, const UINT8 *source, UINT32 complength, UINT8 *dest, UINT32 dstride, UINT32 dxor)
{
/* if the high bit of the first byte is set, we decode losslessly */
if (source[0] & 0x80)
return decode_video_lossless(state, width, height, source, complength, dest, dstride, dxor);
else
return AVCERR_INVALID_DATA;
}
/*-------------------------------------------------
decode_video_lossless - do a lossless video
decoding using deltas and huffman encoding
-------------------------------------------------*/
static avcomp_error decode_video_lossless(avcomp_state *state, int width, int height, const UINT8 *source, UINT32 complength, UINT8 *dest, UINT32 deststride, UINT32 destxor)
{
const UINT8 *sourceend = source + complength;
huffman_context *contexts[4];
huffman_error hufferr;
UINT32 actsize;
/* skip the first byte */
source++;
/* import the tables */
hufferr = huffman_deltarle_import_tree(state->ycontext, source, sourceend - source, &actsize);
if (hufferr != HUFFERR_NONE)
return AVCERR_INVALID_DATA;
source += actsize;
hufferr = huffman_deltarle_import_tree(state->cbcontext, source, sourceend - source, &actsize);
if (hufferr != HUFFERR_NONE)
return AVCERR_INVALID_DATA;
source += actsize;
hufferr = huffman_deltarle_import_tree(state->crcontext, source, sourceend - source, &actsize);
if (hufferr != HUFFERR_NONE)
return AVCERR_INVALID_DATA;
source += actsize;
/* set up the decoding contexts */
contexts[0] = state->ycontext;
contexts[1] = state->cbcontext;
contexts[2] = state->ycontext;
contexts[3] = state->crcontext;
/* decode to the destination */
hufferr = huffman_deltarle_decode_data_interleaved(4, contexts, source, sourceend - source, dest, width * 2, height, deststride, destxor, &actsize);
if (hufferr != HUFFERR_NONE)
return AVCERR_INVALID_DATA;
if (actsize != sourceend - source)
return AVCERR_INVALID_DATA;
return AVCERR_NONE;
}

View File

@ -1,140 +0,0 @@
/***************************************************************************
avcomp.h
Audio/video compression and decompression helpers.
****************************************************************************
Copyright Aaron Giles
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name 'MAME' nor the names of its contributors may be
used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
***************************************************************************/
#ifndef __AVCOMP_H__
#define __AVCOMP_H__
#include "osdcore.h"
#include "bitmap.h"
/***************************************************************************
CONSTANTS
***************************************************************************/
/* errors */
enum _avcomp_error
{
AVCERR_NONE = 0,
AVCERR_INVALID_DATA,
AVCERR_VIDEO_TOO_LARGE,
AVCERR_AUDIO_TOO_LARGE,
AVCERR_METADATA_TOO_LARGE,
AVCERR_OUT_OF_MEMORY,
AVCERR_COMPRESSION_ERROR,
AVCERR_TOO_MANY_CHANNELS,
AVCERR_INVALID_CONFIGURATION
};
typedef enum _avcomp_error avcomp_error;
/* default decompression parameters */
#define AVCOMP_ENABLE_META (1 << 0)
#define AVCOMP_ENABLE_VIDEO (1 << 1)
#define AVCOMP_ENABLE_AUDIO(x) (1 << (2 + (x)))
#define AVCOMP_ENABLE_DEFAULT (~0)
/***************************************************************************
TYPE DEFINITIONS
***************************************************************************/
/* compression configuration */
struct av_codec_compress_config
{
av_codec_compress_config()
: channels(0),
samples(0),
metalength(0),
metadata(NULL)
{
memset(audio, 0, sizeof(audio));
}
bitmap_yuy16 video; /* pointer to video bitmap */
UINT32 channels; /* number of channels */
UINT32 samples; /* number of samples per channel */
INT16 * audio[16]; /* pointer to individual audio channels */
UINT32 metalength; /* length of metadata */
UINT8 * metadata; /* pointer to metadata buffer */
};
/* decompression configuration */
struct av_codec_decompress_config
{
av_codec_decompress_config()
: maxsamples(0),
actsamples(0),
maxmetalength(0),
actmetalength(0),
metadata(NULL)
{
memset(audio, 0, sizeof(audio));
}
bitmap_yuy16 video; /* pointer to video bitmap */
UINT32 maxsamples; /* maximum number of samples per channel */
UINT32 * actsamples; /* actual number of samples per channel */
INT16 * audio[16]; /* pointer to individual audio channels */
UINT32 maxmetalength; /* maximum length of metadata */
UINT32 * actmetalength; /* actual length of metadata */
UINT8 * metadata; /* pointer to metadata buffer */
};
/* opaque state */
struct avcomp_state;
/***************************************************************************
PROTOTYPES
***************************************************************************/
avcomp_state *avcomp_init(UINT32 maxwidth, UINT32 maxheight, UINT32 maxchannels);
void avcomp_free(avcomp_state *state);
void avcomp_config_compress(avcomp_state *state, av_codec_compress_config *config);
void avcomp_config_decompress(avcomp_state *state, av_codec_decompress_config *config);
avcomp_error avcomp_encode_data(avcomp_state *state, const UINT8 *source, UINT8 *dest, UINT32 *complength);
avcomp_error avcomp_decode_data(avcomp_state *state, const UINT8 *source, UINT32 complength, UINT8 *dest);
#endif

977
src/lib/util/avhuff.c Normal file
View File

@ -0,0 +1,977 @@
/***************************************************************************
avhuff.c
Audio/video compression and decompression helpers.
****************************************************************************
Copyright Aaron Giles
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name 'MAME' nor the names of its contributors may be
used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
****************************************************************************
Each frame is compressed as a unit. The raw data is of the form:
(all multibyte values are stored in big-endian format)
+00 = 'chav' (4 bytes) - fixed header data to identify the format
+04 = metasize (1 byte) - size of metadata in bytes (max=255 bytes)
+05 = channels (1 byte) - number of audio channels
+06 = samples (2 bytes) - number of samples per audio stream
+08 = width (2 bytes) - width of video data
+0A = height (2 bytes) - height of video data
+0C = <metadata> - as raw bytes
<audio stream 0> - as signed 16-bit samples
<audio stream 1> - as signed 16-bit samples
...
<video data> - as a raw array of 8-bit YUY data in (Cb,Y,Cr,Y) order
When compressed, the data is stored as follows:
(all multibyte values are stored in big-endian format)
+00 = metasize (1 byte) - size of metadata in bytes
+01 = channels (1 byte) - number of audio channels
+02 = samples (2 bytes) - number of samples per audio stream
+04 = width (2 bytes) - width of video data
+06 = height (2 bytes) - height of video data
+08 = audio huffman size (2 bytes) - size of audio huffman tables
(0x0000 => uncompressed deltas are used)
+0A = str0size (2 bytes) - compressed size of stream 0
+0C = str1size (2 bytes) - compressed size of stream 1
...
<metadata> - as raw data
<audio huffman table> - Huffman table for audio decoding
<audio stream 0 data> - Huffman-compressed deltas
<audio stream 1 data> - Huffman-compressed deltas
<...>
<video huffman tables> - Huffman tables for video decoding
<video data> - compressed data
****************************************************************************
Attempted techniques that have not been worthwhile:
* Attempted to use integer DCTs from the IJG code; even the "slow"
variants produce a lot of error and thus kill our compression ratio,
since our compression is based on error not bitrate.
* Tried various other predictors for the lossless video encoding, but
none tended to give any significant gain over predicting the
previous pixel.
***************************************************************************/
#include "avhuff.h"
#include "huffman.h"
#include "chd.h"
#include <math.h>
#include <stdlib.h>
#include <new>
//**************************************************************************
// INLINE FUNCTIONS
//**************************************************************************
//-------------------------------------------------
// code_to_rlecount - number of RLE repetitions
// encoded in a given byte
//-------------------------------------------------
inline int code_to_rlecount(int code)
{
if (code == 0x00)
return 1;
if (code <= 0x107)
return 8 + (code - 0x100);
return 16 << (code - 0x108);
}
//-------------------------------------------------
// rlecount_to_byte - return a byte encoding
// the maximum RLE count less than or equal to
// the provided amount
//-------------------------------------------------
inline int rlecount_to_code(int rlecount)
{
if (rlecount >= 2048)
return 0x10f;
if (rlecount >= 1024)
return 0x10e;
if (rlecount >= 512)
return 0x10d;
if (rlecount >= 256)
return 0x10c;
if (rlecount >= 128)
return 0x10b;
if (rlecount >= 64)
return 0x10a;
if (rlecount >= 32)
return 0x109;
if (rlecount >= 16)
return 0x108;
if (rlecount >= 8)
return 0x100 + (rlecount - 8);
return 0x00;
}
//-------------------------------------------------
// encode_one - encode data
//-------------------------------------------------
inline void avhuff_encoder::deltarle_encoder::encode_one(bitstream_out &bitbuf, UINT16 *&rleptr)
{
// return RLE data if we still have some
if (m_rlecount != 0)
{
m_rlecount--;
return;
}
// fetch the data and process
UINT16 data = *rleptr++;
m_encoder.encode_one(bitbuf, data);
if (data >= 0x100)
m_rlecount = code_to_rlecount(data) - 1;
}
//-------------------------------------------------
// decode_one - decode data
//-------------------------------------------------
inline UINT32 avhuff_decoder::deltarle_decoder::decode_one(bitstream_in &bitbuf)
{
// return RLE data if we still have some
if (m_rlecount != 0)
{
m_rlecount--;
return m_prevdata;
}
// fetch the data and process
int data = m_decoder.decode_one(bitbuf);
if (data < 0x100)
{
m_prevdata += UINT8(data);
return m_prevdata;
}
else
{
m_rlecount = code_to_rlecount(data);
m_rlecount--;
return m_prevdata;
}
}
//**************************************************************************
// AVHUFF ENCODER
//**************************************************************************
//-------------------------------------------------
// avhuff_encoder - constructor
//-------------------------------------------------
avhuff_encoder::avhuff_encoder()
{
m_flac_encoder.set_sample_rate(48000);
m_flac_encoder.set_num_channels(1);
m_flac_encoder.set_strip_metadata(true);
}
//-------------------------------------------------
// encode_data - encode a block of data into a
// compressed data stream
//-------------------------------------------------
avhuff_error avhuff_encoder::encode_data(const UINT8 *source, UINT8 *dest, UINT32 &complength)
{
// validate the header
if (source[0] != 'c' || source[1] != 'h' || source[2] != 'a' || source[3] != 'v')
return AVHERR_INVALID_DATA;
// extract info from the header
UINT32 metasize = source[4];
UINT32 channels = source[5];
UINT32 samples = (source[6] << 8) + source[7];
UINT32 width = (source[8] << 8) + source[9];
UINT32 height = (source[10] << 8) + source[11];
source += 12;
// write the basics to the new header
dest[0] = metasize;
dest[1] = channels;
dest[2] = samples >> 8;
dest[3] = samples;
dest[4] = width >> 8;
dest[5] = width;
dest[6] = height >> 8;
dest[7] = height;
// starting offsets
UINT32 dstoffs = 10 + 2 * channels;
// copy the metadata first
if (metasize > 0)
{
memcpy(dest + dstoffs, source, metasize);
source += metasize;
dstoffs += metasize;
}
// encode the audio channels
if (channels > 0)
{
// encode the audio
avhuff_error err = encode_audio(source, channels, samples, dest + dstoffs, &dest[8]);
source += channels * samples * 2;
if (err != AVHERR_NONE)
return err;
// advance the pointers past the data
UINT16 treesize = (dest[8] << 8) + dest[9];
if (treesize != 0xffff)
dstoffs += treesize;
for (int chnum = 0; chnum < channels; chnum++)
dstoffs += (dest[10 + 2 * chnum] << 8) + dest[11 + 2 * chnum];
}
// encode the video data
if (width > 0 && height > 0)
{
// encode the video
UINT32 vidlength = 0;
avhuff_error err = encode_video(source, width, height, dest + dstoffs, vidlength);
if (err != AVHERR_NONE)
return err;
// advance the pointers past the data
dstoffs += vidlength;
}
// set the total compression
complength = dstoffs;
return AVHERR_NONE;
}
//-------------------------------------------------
// raw_data_size - return the raw data size of
// a raw stream based on the header
//-------------------------------------------------
UINT32 avhuff_encoder::raw_data_size(const UINT8 *data)
{
// make sure we have a correct header
int size = 0;
if (data[0] == 'c' && data[1] == 'h' && data[2] == 'a' && data[3] == 'v')
{
// add in header size plus metadata length
size = 12 + data[4];
// add in channels * samples
size += 2 * data[5] * ((data[6] << 8) + data[7]);
// add in 2 * width * height
size += 2 * ((data[8] << 8) + data[9]) * (((data[10] << 8) + data[11]) & 0x7fff);
}
return size;
}
//-------------------------------------------------
// assemble_data - assemble a datastream from raw
// bits
//-------------------------------------------------
avhuff_error avhuff_encoder::assemble_data(UINT8 *dest, UINT32 dlength, bitmap_yuy16 &bitmap, UINT8 channels, UINT32 numsamples, INT16 **samples, UINT8 *metadata, UINT32 metadatasize)
{
// sanity check the inputs
if (metadatasize > 255)
return AVHERR_METADATA_TOO_LARGE;
if (numsamples > 65535)
return AVHERR_AUDIO_TOO_LARGE;
if (bitmap.width() > 65535 || bitmap.height() > 65535)
return AVHERR_VIDEO_TOO_LARGE;
if (dlength < 12 + metadatasize + numsamples * channels * 2 + bitmap.width() * bitmap.height() * 2)
return AVHERR_BUFFER_TOO_SMALL;
// fill in the header
*dest++ = 'c';
*dest++ = 'h';
*dest++ = 'a';
*dest++ = 'v';
*dest++ = metadatasize;
*dest++ = channels;
*dest++ = numsamples >> 8;
*dest++ = numsamples & 0xff;
*dest++ = bitmap.width() >> 8;
*dest++ = bitmap.width() & 0xff;
*dest++ = bitmap.height() >> 8;
*dest++ = bitmap.height() & 0xff;
// copy the metadata
if (metadatasize > 0)
memcpy(dest, metadata, metadatasize);
dest += metadatasize;
// copy the audio streams
for (UINT8 curchan = 0; curchan < channels; curchan++)
for (UINT32 cursamp = 0; cursamp < numsamples; cursamp++)
{
*dest++ = samples[curchan][cursamp] >> 8;
*dest++ = samples[curchan][cursamp] & 0xff;
}
// copy the video data
for (INT32 y = 0; y < bitmap.height(); y++)
{
UINT16 *src = &bitmap.pix(y);
for (INT32 x = 0; x < bitmap.width(); x++)
{
*dest++ = src[x] >> 8;
*dest++ = src[x] & 0xff;
}
}
return AVHERR_NONE;
}
//-------------------------------------------------
// encode_audio - encode raw audio data to the
// destination
//-------------------------------------------------
avhuff_error avhuff_encoder::encode_audio(const UINT8 *source, int channels, int samples, UINT8 *dest, UINT8 *sizes)
{
#if AVHUFF_USE_FLAC
// input data is big-endian; determine our platform endianness
UINT16 be_test = 0;
*(UINT8 *)&be_test = 1;
bool swap_endian = (be_test == 1);
// set huffman tree size to 0xffff to indicate FLAC
sizes[0] = 0xff;
sizes[1] = 0xff;
// set the block size for this round and iterate over channels
m_flac_encoder.set_block_size(samples);
for (int chnum = 0; chnum < channels; chnum++)
{
// encode the data
m_flac_encoder.reset(dest, samples * 2);
if (!m_flac_encoder.encode_interleaved(reinterpret_cast<const INT16 *>(source) + chnum * samples, samples, swap_endian))
return AVHERR_COMPRESSION_ERROR;
// set the size for this channel
UINT32 cursize = m_flac_encoder.finish();
sizes[chnum * 2 + 2] = cursize >> 8;
sizes[chnum * 2 + 3] = cursize;
dest += cursize;
}
#else
// expand the delta buffer if needed
m_audiobuffer.resize(channels * samples * 2);
UINT8 *deltabuf = m_audiobuffer;
// iterate over channels to compute deltas
m_audiohi_encoder.histo_reset();
m_audiolo_encoder.histo_reset();
for (int chnum = 0; chnum < channels; chnum++)
{
// extract audio data into hi and lo deltas stored in big-endian order
INT16 prevsample = 0;
for (int sampnum = 0; sampnum < samples; sampnum++)
{
INT16 newsample = (source[0] << 8) | source[1];
source += 2;
INT16 delta = newsample - prevsample;
prevsample = newsample;
m_audiohi_encoder.histo_one(*deltabuf++ = delta >> 8);
m_audiolo_encoder.histo_one(*deltabuf++ = delta);
}
}
// compute the trees
huffman_error hufferr = m_audiohi_encoder.compute_tree_from_histo();
if (hufferr != HUFFERR_NONE)
return AVHERR_COMPRESSION_ERROR;
hufferr = m_audiolo_encoder.compute_tree_from_histo();
if (hufferr != HUFFERR_NONE)
return AVHERR_COMPRESSION_ERROR;
// export the trees to the output
bitstream_out bitbuf(dest, 2 * channels * samples);
hufferr = m_audiohi_encoder.export_tree_rle(bitbuf);
if (hufferr != HUFFERR_NONE)
return AVHERR_COMPRESSION_ERROR;
bitbuf.flush();
hufferr = m_audiolo_encoder.export_tree_rle(bitbuf);
if (hufferr != HUFFERR_NONE)
return AVHERR_COMPRESSION_ERROR;
// note the size of the two trees
UINT32 huffsize = bitbuf.flush();
sizes[0] = huffsize >> 8;
sizes[1] = huffsize;
// iterate over channels
UINT32 totalsize = huffsize;
int chnum;
for (chnum = 0; chnum < channels; chnum++)
{
// encode the data
const UINT8 *input = m_audiobuffer + chnum * samples * 2;
for (int sampnum = 0; sampnum < samples; sampnum++)
{
m_audiohi_encoder.encode_one(bitbuf, *input++);
m_audiolo_encoder.encode_one(bitbuf, *input++);
}
// store the size of this stream
UINT32 cursize = bitbuf.flush() - totalsize;
totalsize += cursize;
if (totalsize >= channels * samples * 2)
break;
sizes[chnum * 2 + 2] = cursize >> 8;
sizes[chnum * 2 + 3] = cursize;
}
// if we ran out of room, throw it all away and just store raw
if (chnum < channels)
{
memcpy(dest, m_audiobuffer, channels * samples * 2);
UINT32 size = samples * 2;
sizes[0] = sizes[1] = 0;
for (chnum = 0; chnum < channels; chnum++)
{
sizes[chnum * 2 + 2] = size >> 8;
sizes[chnum * 2 + 3] = size;
}
}
#endif
return AVHERR_NONE;
}
//-------------------------------------------------
// encode_video - encode raw video data to the
// destination
//-------------------------------------------------
avhuff_error avhuff_encoder::encode_video(const UINT8 *source, int width, int height, UINT8 *dest, UINT32 &complength)
{
// only lossless supported at this time
return encode_video_lossless(source, width, height, dest, complength);
}
//-------------------------------------------------
// encode_video_lossless - do a lossless video
// encoding using deltas and huffman encoding
//-------------------------------------------------
avhuff_error avhuff_encoder::encode_video_lossless(const UINT8 *source, int width, int height, UINT8 *dest, UINT32 &complength)
{
// set up the output; first byte is 0x80 to indicate lossless encoding
bitstream_out bitbuf(dest, width * height * 2);
bitbuf.write(0x80, 8);
// compute the histograms for the data
UINT16 *yrle = m_ycontext.rle_and_histo_bitmap(source + 0, width, 2, height);
UINT16 *cbrle = m_cbcontext.rle_and_histo_bitmap(source + 1, width / 2, 4, height);
UINT16 *crrle = m_crcontext.rle_and_histo_bitmap(source + 3, width / 2, 4, height);
// export the trees to the data stream
huffman_error hufferr = m_ycontext.export_tree_rle(bitbuf);
if (hufferr != HUFFERR_NONE)
return AVHERR_COMPRESSION_ERROR;
bitbuf.flush();
hufferr = m_cbcontext.export_tree_rle(bitbuf);
if (hufferr != HUFFERR_NONE)
return AVHERR_COMPRESSION_ERROR;
bitbuf.flush();
hufferr = m_crcontext.export_tree_rle(bitbuf);
if (hufferr != HUFFERR_NONE)
return AVHERR_COMPRESSION_ERROR;
bitbuf.flush();
// encode the data using the trees
for (UINT32 sy = 0; sy < height; sy++)
{
m_ycontext.flush_rle();
m_cbcontext.flush_rle();
m_crcontext.flush_rle();
for (UINT32 sx = 0; sx < width / 2; sx++)
{
m_ycontext.encode_one(bitbuf, yrle);
m_cbcontext.encode_one(bitbuf, cbrle);
m_ycontext.encode_one(bitbuf, yrle);
m_crcontext.encode_one(bitbuf, crrle);
}
}
// set the final length
complength = bitbuf.flush();
return AVHERR_NONE;
}
//**************************************************************************
// DELTA-RLE ENCODER
//**************************************************************************
//-------------------------------------------------
// rle_and_histo_bitmap - RLE compress and
// histogram a bitmap's worth of data
//-------------------------------------------------
UINT16 *avhuff_encoder::deltarle_encoder::rle_and_histo_bitmap(const UINT8 *source, UINT32 items_per_row, UINT32 item_advance, UINT32 row_count)
{
// resize our RLE buffer
m_rlebuffer.resize(items_per_row * row_count);
UINT16 *dest = m_rlebuffer;
// iterate over rows
m_encoder.histo_reset();
UINT8 prevdata = 0;
for (UINT32 row = 0; row < row_count; row++)
{
const UINT8 *end = source + items_per_row * item_advance;
for ( ; source < end; source += item_advance)
{
// fetch current data
UINT8 curdelta = *source - prevdata;
prevdata = *source;
// 0 deltas scan forward for a count
if (curdelta == 0)
{
int zerocount = 1;
// count the number of consecutive values
const UINT8 *scandata;
for (scandata = source + item_advance; scandata < end; scandata += item_advance)
if (*scandata == prevdata)
zerocount++;
else
break;
// if we hit the end of a row, maximize the count
if (scandata >= end && zerocount >= 8)
zerocount = 100000;
// encode the maximal count we can
int rlecode = rlecount_to_code(zerocount);
m_encoder.histo_one(*dest++ = rlecode);
// advance past the run
source += (code_to_rlecount(rlecode) - 1) * item_advance;
}
// otherwise, encode the actual data
else
m_encoder.histo_one(*dest++ = curdelta);
}
// advance to the next row
source = end;
}
// compute the tree for our histogram
m_encoder.compute_tree_from_histo();
return m_rlebuffer;
}
//**************************************************************************
// AVHUFF DECODER
//**************************************************************************
//-------------------------------------------------
// avhuff_decoder - constructor
//-------------------------------------------------
avhuff_decoder::avhuff_decoder()
{
}
//-------------------------------------------------
// configure - configure decompression parameters
//-------------------------------------------------
void avhuff_decoder::configure(const avhuff_decompress_config &config)
{
m_config.video.wrap(config.video, config.video.cliprect());
m_config.maxsamples = config.maxsamples;
m_config.actsamples = config.actsamples;
memcpy(m_config.audio, config.audio, sizeof(m_config.audio));
m_config.maxmetalength = config.maxmetalength;
m_config.actmetalength = config.actmetalength;
m_config.metadata = config.metadata;
}
//-------------------------------------------------
// decode_data - decode both audio and video from
// a raw data stream
//-------------------------------------------------
avhuff_error avhuff_decoder::decode_data(const UINT8 *source, UINT32 complength, UINT8 *dest)
{
// extract info from the header
if (complength < 8)
return AVHERR_INVALID_DATA;
UINT32 metasize = source[0];
UINT32 channels = source[1];
UINT32 samples = (source[2] << 8) + source[3];
UINT32 width = (source[4] << 8) + source[5];
UINT32 height = (source[6] << 8) + source[7];
// validate that the sizes make sense
if (complength < 10 + 2 * channels)
return AVHERR_INVALID_DATA;
UINT32 totalsize = 10 + 2 * channels;
totalsize += (source[8] << 8) | source[9];
for (int chnum = 0; chnum < channels; chnum++)
totalsize += (source[10 + 2 * chnum] << 8) | source[11 + 2 * chnum];
if (totalsize >= complength)
return AVHERR_INVALID_DATA;
// starting offsets
UINT32 srcoffs = 10 + 2 * channels;
// if we are decoding raw, set up the output parameters
UINT8 *metastart, *videostart, *audiostart[16];
UINT32 audioxor, videoxor, videostride;
if (dest != NULL)
{
// create a header
dest[0] = 'c';
dest[1] = 'h';
dest[2] = 'a';
dest[3] = 'v';
dest[4] = metasize;
dest[5] = channels;
dest[6] = samples >> 8;
dest[7] = samples;
dest[8] = width >> 8;
dest[9] = width;
dest[10] = height >> 8;
dest[11] = height;
dest += 12;
// determine the start of each piece of data
metastart = dest;
dest += metasize;
for (int chnum = 0; chnum < channels; chnum++)
{
audiostart[chnum] = dest;
dest += 2 * samples;
}
videostart = dest;
// data is assumed to be big-endian already
audioxor = videoxor = 0;
videostride = 2 * width;
}
// otherwise, extract from the state
else
{
// determine the start of each piece of data
metastart = m_config.metadata;
for (int chnum = 0; chnum < channels; chnum++)
audiostart[chnum] = (UINT8 *)m_config.audio[chnum];
videostart = (m_config.video.valid()) ? reinterpret_cast<UINT8 *>(&m_config.video.pix(0)) : NULL;
videostride = (m_config.video.valid()) ? m_config.video.rowpixels() * 2 : 0;
// data is assumed to be native-endian
UINT16 betest = 0;
*(UINT8 *)&betest = 1;
audioxor = videoxor = (betest == 1) ? 1 : 0;
// verify against sizes
if (m_config.video.valid() && (m_config.video.width() < width || m_config.video.height() < height))
return AVHERR_VIDEO_TOO_LARGE;
for (int chnum = 0; chnum < channels; chnum++)
if (m_config.audio[chnum] != NULL && m_config.maxsamples < samples)
return AVHERR_AUDIO_TOO_LARGE;
if (m_config.metadata != NULL && m_config.maxmetalength < metasize)
return AVHERR_METADATA_TOO_LARGE;
// set the output values
if (m_config.actsamples != NULL)
*m_config.actsamples = samples;
if (m_config.actmetalength != NULL)
*m_config.actmetalength = metasize;
}
// copy the metadata first
if (metasize > 0)
{
if (metastart != NULL)
memcpy(metastart, source + srcoffs, metasize);
srcoffs += metasize;
}
// decode the audio channels
if (channels > 0)
{
// decode the audio
avhuff_error err = decode_audio(channels, samples, source + srcoffs, audiostart, audioxor, &source[8]);
if (err != AVHERR_NONE)
return err;
// advance the pointers past the data
UINT32 treesize = (source[8] << 8) + source[9];
if (treesize != 0xffff)
srcoffs += treesize;
for (int chnum = 0; chnum < channels; chnum++)
srcoffs += (source[10 + 2 * chnum] << 8) + source[11 + 2 * chnum];
}
// decode the video data
if (width > 0 && height > 0 && videostart != NULL)
{
// decode the video
avhuff_error err = decode_video(width, height, source + srcoffs, complength - srcoffs, videostart, videostride, videoxor);
if (err != AVHERR_NONE)
return err;
}
return AVHERR_NONE;
}
//-------------------------------------------------
// decode_audio - decode audio from a compressed
// data stream
//-------------------------------------------------
avhuff_error avhuff_decoder::decode_audio(int channels, int samples, const UINT8 *source, UINT8 **dest, UINT32 dxor, const UINT8 *sizes)
{
// extract the huffman trees
UINT16 treesize = (sizes[0] << 8) | sizes[1];
#if AVHUFF_USE_FLAC
// if the tree size is 0xffff, the streams are FLAC-encoded
if (treesize == 0xffff)
{
// output data is big-endian; determine our platform endianness
UINT16 be_test = 0;
*(UINT8 *)&be_test = 1;
bool swap_endian = (be_test == 1);
if (dxor != 0)
swap_endian = !swap_endian;
// loop over channels
for (int chnum = 0; chnum < channels; chnum++)
{
// extract the size of this channel
UINT16 size = (sizes[chnum * 2 + 2] << 8) | sizes[chnum * 2 + 3];
// only process if the data is requested
UINT8 *curdest = dest[chnum];
if (curdest != NULL)
{
// reset and decode
if (!m_flac_decoder.reset(48000, 1, samples, source, size))
throw CHDERR_DECOMPRESSION_ERROR;
if (!m_flac_decoder.decode_interleaved(reinterpret_cast<INT16 *>(curdest), samples, swap_endian))
throw CHDERR_DECOMPRESSION_ERROR;
// finish up
m_flac_decoder.finish();
}
// advance to the next channel's data
source += size;
}
return AVHERR_NONE;
}
#endif
// if we have a non-zero tree size, extract the trees
if (treesize != 0)
{
bitstream_in bitbuf(source, treesize);
huffman_error hufferr = m_audiohi_decoder.import_tree_rle(bitbuf);
if (hufferr != HUFFERR_NONE)
return AVHERR_INVALID_DATA;
bitbuf.flush();
hufferr = m_audiolo_decoder.import_tree_rle(bitbuf);
if (hufferr != HUFFERR_NONE)
return AVHERR_INVALID_DATA;
if (bitbuf.flush() != treesize)
return AVHERR_INVALID_DATA;
source += treesize;
}
// loop over channels
for (int chnum = 0; chnum < channels; chnum++)
{
// extract the size of this channel
UINT16 size = (sizes[chnum * 2 + 2] << 8) | sizes[chnum * 2 + 3];
// only process if the data is requested
UINT8 *curdest = dest[chnum];
if (curdest != NULL)
{
INT16 prevsample = 0;
// if no huffman length, just copy the data
if (treesize == 0)
{
const UINT8 *cursource = source;
for (int sampnum = 0; sampnum < samples; sampnum++)
{
INT16 delta = (cursource[0] << 8) | cursource[1];
cursource += 2;
INT16 newsample = prevsample + delta;
prevsample = newsample;
curdest[0 ^ dxor] = newsample >> 8;
curdest[1 ^ dxor] = newsample;
curdest += 2;
}
}
// otherwise, Huffman-decode the data
else
{
bitstream_in bitbuf(source, size);
for (int sampnum = 0; sampnum < samples; sampnum++)
{
INT16 delta = m_audiohi_decoder.decode_one(bitbuf) << 8;
delta |= m_audiolo_decoder.decode_one(bitbuf);
INT16 newsample = prevsample + delta;
prevsample = newsample;
curdest[0 ^ dxor] = newsample >> 8;
curdest[1 ^ dxor] = newsample;
curdest += 2;
}
if (bitbuf.overflow())
return AVHERR_INVALID_DATA;
}
}
// advance to the next channel's data
source += size;
}
return AVHERR_NONE;
}
//-------------------------------------------------
// decode_video - decode video from a compressed
// data stream
//-------------------------------------------------
avhuff_error avhuff_decoder::decode_video(int width, int height, const UINT8 *source, UINT32 complength, UINT8 *dest, UINT32 dstride, UINT32 dxor)
{
// if the high bit of the first byte is set, we decode losslessly
if (source[0] & 0x80)
return decode_video_lossless(width, height, source, complength, dest, dstride, dxor);
else
return AVHERR_INVALID_DATA;
}
//-------------------------------------------------
// decode_video_lossless - do a lossless video
// decoding using deltas and huffman encoding
//-------------------------------------------------
avhuff_error avhuff_decoder::decode_video_lossless(int width, int height, const UINT8 *source, UINT32 complength, UINT8 *dest, UINT32 dstride, UINT32 dxor)
{
// skip the first byte
bitstream_in bitbuf(source, complength);
bitbuf.read(8);
// import the tables
huffman_error hufferr = m_ycontext.import_tree_rle(bitbuf);
if (hufferr != HUFFERR_NONE)
return AVHERR_INVALID_DATA;
bitbuf.flush();
hufferr = m_cbcontext.import_tree_rle(bitbuf);
if (hufferr != HUFFERR_NONE)
return AVHERR_INVALID_DATA;
bitbuf.flush();
hufferr = m_crcontext.import_tree_rle(bitbuf);
if (hufferr != HUFFERR_NONE)
return AVHERR_INVALID_DATA;
bitbuf.flush();
// decode to the destination
m_ycontext.reset();
m_cbcontext.reset();
m_crcontext.reset();
for (UINT32 dy = 0; dy < height; dy++)
{
UINT8 *row = dest + dy * dstride;
for (UINT32 dx = 0; dx < width / 2; dx++)
{
row[0 ^ dxor] = m_ycontext.decode_one(bitbuf);
row[1 ^ dxor] = m_cbcontext.decode_one(bitbuf);
row[2 ^ dxor] = m_ycontext.decode_one(bitbuf);
row[3 ^ dxor] = m_crcontext.decode_one(bitbuf);
row += 4;
}
m_ycontext.flush_rle();
m_cbcontext.flush_rle();
m_crcontext.flush_rle();
}
// check for errors if we overflowed or decoded too little data
if (bitbuf.overflow() || bitbuf.flush() != complength)
return AVHERR_INVALID_DATA;
return AVHERR_NONE;
}

231
src/lib/util/avhuff.h Normal file
View File

@ -0,0 +1,231 @@
/***************************************************************************
avhuff.h
Audio/video compression and decompression helpers.
****************************************************************************
Copyright Aaron Giles
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name 'MAME' nor the names of its contributors may be
used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
***************************************************************************/
#pragma once
#ifndef __AVHUFF_H__
#define __AVHUFF_H__
#include "osdcore.h"
#include "coretmpl.h"
#include "bitmap.h"
#include "huffman.h"
#include "flac.h"
//**************************************************************************
// CONSTANTS
//**************************************************************************
#define AVHUFF_USE_FLAC (1)
// errors
enum avhuff_error
{
AVHERR_NONE = 0,
AVHERR_INVALID_DATA,
AVHERR_VIDEO_TOO_LARGE,
AVHERR_AUDIO_TOO_LARGE,
AVHERR_METADATA_TOO_LARGE,
AVHERR_OUT_OF_MEMORY,
AVHERR_COMPRESSION_ERROR,
AVHERR_TOO_MANY_CHANNELS,
AVHERR_INVALID_CONFIGURATION,
AVHERR_INVALID_PARAMETER,
AVHERR_BUFFER_TOO_SMALL
};
//**************************************************************************
// TYPE DEFINITIONS
//**************************************************************************
// ======================> av_codec_decompress_config
// decompression configuration
class avhuff_decompress_config
{
public:
avhuff_decompress_config()
: maxsamples(0),
actsamples(NULL),
maxmetalength(0),
actmetalength(NULL),
metadata(NULL)
{
memset(audio, 0, sizeof(audio));
}
bitmap_yuy16 video; // pointer to video bitmap
UINT32 maxsamples; // maximum number of samples per channel
UINT32 * actsamples; // actual number of samples per channel
INT16 * audio[16]; // pointer to individual audio channels
UINT32 maxmetalength; // maximum length of metadata
UINT32 * actmetalength; // actual length of metadata
UINT8 * metadata; // pointer to metadata buffer
};
// ======================> avhuff_encoder
// core state for the codec
class avhuff_encoder
{
public:
// construction/destruction
avhuff_encoder();
// encode/decode
avhuff_error encode_data(const UINT8 *source, UINT8 *dest, UINT32 &complength);
// static helpers
static UINT32 raw_data_size(const UINT8 *data);
static UINT32 raw_data_size(UINT32 width, UINT32 height, UINT8 channels, UINT32 numsamples, UINT32 metadatasize = 0) { return 12 + channels * numsamples * 2 + width * height * 2; }
static avhuff_error assemble_data(UINT8 *dest, UINT32 dlength, bitmap_yuy16 &bitmap, UINT8 channels, UINT32 numsamples, INT16 **samples, UINT8 *metadata = NULL, UINT32 metadatasize = 0);
private:
// delta-RLE Huffman encoder
class deltarle_encoder
{
public:
// construction/destruction
deltarle_encoder()
: m_rlecount(0) { }
// histogramming
UINT16 *rle_and_histo_bitmap(const UINT8 *source, UINT32 items_per_row, UINT32 item_advance, UINT32 row_count);
// encoding
void flush_rle() { m_rlecount = 0; }
void encode_one(bitstream_out &bitbuf, UINT16 *&rleptr);
huffman_error export_tree_rle(bitstream_out &bitbuf) { return m_encoder.export_tree_rle(bitbuf); }
private:
// internal state
int m_rlecount;
huffman_encoder<256 + 16> m_encoder;
dynamic_array<UINT16> m_rlebuffer;
};
// internal helpers
avhuff_error encode_audio(const UINT8 *source, int channels, int samples, UINT8 *dest, UINT8 *sizes);
avhuff_error encode_video(const UINT8 *source, int width, int height, UINT8 *dest, UINT32 &complength);
avhuff_error encode_video_lossless(const UINT8 *source, int width, int height, UINT8 *dest, UINT32 &complength);
// video encoding contexts
deltarle_encoder m_ycontext;
deltarle_encoder m_cbcontext;
deltarle_encoder m_crcontext;
// audio encoding contexts
dynamic_buffer m_audiobuffer;
#if AVHUFF_USE_FLAC
flac_encoder m_flac_encoder;
#else
huffman_8bit_encoder m_audiohi_encoder;
huffman_8bit_encoder m_audiolo_encoder;
#endif
};
// ======================> avhuff_decoder
// core state for the codec
class avhuff_decoder
{
public:
// construction/destruction
avhuff_decoder();
// configuration
void configure(const avhuff_decompress_config &config);
// encode/decode
avhuff_error decode_data(const UINT8 *source, UINT32 complength, UINT8 *dest);
private:
// delta-RLE Huffman decoder
class deltarle_decoder
{
public:
// construction/destruction
deltarle_decoder()
: m_rlecount(0), m_prevdata(0) { }
// general
void reset() { m_rlecount = m_prevdata = 0; }
// decoding
void flush_rle() { m_rlecount = 0; }
UINT32 decode_one(bitstream_in &bitbuf);
huffman_error import_tree_rle(bitstream_in &bitbuf) { return m_decoder.import_tree_rle(bitbuf); }
private:
// internal state
int m_rlecount;
UINT8 m_prevdata;
huffman_decoder<256 + 16> m_decoder;
};
// internal helpers
avhuff_error decode_audio(int channels, int samples, const UINT8 *source, UINT8 **dest, UINT32 dxor, const UINT8 *sizes);
avhuff_error decode_video(int width, int height, const UINT8 *source, UINT32 complength, UINT8 *dest, UINT32 dstride, UINT32 dxor);
avhuff_error decode_video_lossless(int width, int height, const UINT8 *source, UINT32 complength, UINT8 *dest, UINT32 dstride, UINT32 dxor);
// internal state
avhuff_decompress_config m_config;
// video decoding contexts
deltarle_decoder m_ycontext;
deltarle_decoder m_cbcontext;
deltarle_decoder m_crcontext;
// audio decoding contexts
huffman_8bit_decoder m_audiohi_decoder;
huffman_8bit_decoder m_audiolo_decoder;
#if AVHUFF_USE_FLAC
flac_decoder m_flac_decoder;
#endif
};
#endif

View File

@ -1573,7 +1573,7 @@ static avi_error parse_indx_chunk(avi_file *file, avi_stream *stream, avi_chunk
{
const UINT8 *base = &chunkdata[24 + entry * 4 * longs_per_entry];
UINT32 offset = fetch_32bits(&base[0]);
UINT32 size = fetch_32bits(&base[4]);
UINT32 size = fetch_32bits(&base[4]) & 0x7fffffff; // bit 31 == NOT a keyframe
/* set the info for this chunk and advance */
avierr = set_stream_chunk_info(stream, stream->chunks++, baseoffset + offset - 8, size + 8);

View File

@ -271,7 +271,7 @@ void bitmap_t::wrap(void *base, int width, int height, int rowpixels)
// bitmap does not own the memory
//-------------------------------------------------
void bitmap_t::wrap(bitmap_t &source, const rectangle &subrect)
void bitmap_t::wrap(const bitmap_t &source, const rectangle &subrect)
{
assert(m_format == source.m_format);
assert(m_bpp == source.m_bpp);

View File

@ -177,7 +177,7 @@ public:
protected:
// for use by subclasses only to ensure type correctness
void wrap(void *base, int width, int height, int rowpixels);
void wrap(bitmap_t &source, const rectangle &subrect);
void wrap(const bitmap_t &source, const rectangle &subrect);
private:
// internal helpers
@ -306,7 +306,7 @@ public:
bitmap_ind8(UINT8 *base, int width, int height, int rowpixels) : bitmap8_t(k_bitmap_format, base, width, height, rowpixels) { }
bitmap_ind8(bitmap_ind8 &source, const rectangle &subrect) : bitmap8_t(k_bitmap_format, source, subrect) { }
void wrap(UINT8 *base, int width, int height, int rowpixels) { bitmap_t::wrap(base, width, height, rowpixels); }
void wrap(bitmap_ind8 &source, const rectangle &subrect) { bitmap_t::wrap(static_cast<bitmap_t &>(source), subrect); }
void wrap(const bitmap_ind8 &source, const rectangle &subrect) { bitmap_t::wrap(static_cast<const bitmap_t &>(source), subrect); }
// getters
bitmap_format format() const { return k_bitmap_format; }
@ -323,7 +323,7 @@ public:
bitmap_ind16(UINT16 *base, int width, int height, int rowpixels) : bitmap16_t(k_bitmap_format, base, width, height, rowpixels) { }
bitmap_ind16(bitmap_ind16 &source, const rectangle &subrect) : bitmap16_t(k_bitmap_format, source, subrect) { }
void wrap(UINT16 *base, int width, int height, int rowpixels) { bitmap_t::wrap(base, width, height, rowpixels); }
void wrap(bitmap_ind8 &source, const rectangle &subrect) { bitmap_t::wrap(static_cast<bitmap_t &>(source), subrect); }
void wrap(const bitmap_ind8 &source, const rectangle &subrect) { bitmap_t::wrap(static_cast<const bitmap_t &>(source), subrect); }
// getters
bitmap_format format() const { return k_bitmap_format; }
@ -340,7 +340,7 @@ public:
bitmap_ind32(UINT32 *base, int width, int height, int rowpixels) : bitmap32_t(k_bitmap_format, base, width, height, rowpixels) { }
bitmap_ind32(bitmap_ind32 &source, const rectangle &subrect) : bitmap32_t(k_bitmap_format, source, subrect) { }
void wrap(UINT32 *base, int width, int height, int rowpixels) { bitmap_t::wrap(base, width, height, rowpixels); }
void wrap(bitmap_ind8 &source, const rectangle &subrect) { bitmap_t::wrap(static_cast<bitmap_t &>(source), subrect); }
void wrap(const bitmap_ind8 &source, const rectangle &subrect) { bitmap_t::wrap(static_cast<const bitmap_t &>(source), subrect); }
// getters
bitmap_format format() const { return k_bitmap_format; }
@ -357,7 +357,7 @@ public:
bitmap_ind64(UINT64 *base, int width, int height, int rowpixels) : bitmap64_t(k_bitmap_format, base, width, height, rowpixels) { }
bitmap_ind64(bitmap_ind64 &source, const rectangle &subrect) : bitmap64_t(k_bitmap_format, source, subrect) { }
void wrap(UINT64 *base, int width, int height, int rowpixels) { bitmap_t::wrap(base, width, height, rowpixels); }
void wrap(bitmap_ind8 &source, const rectangle &subrect) { bitmap_t::wrap(static_cast<bitmap_t &>(source), subrect); }
void wrap(const bitmap_ind8 &source, const rectangle &subrect) { bitmap_t::wrap(static_cast<const bitmap_t &>(source), subrect); }
// getters
bitmap_format format() const { return k_bitmap_format; }
@ -377,7 +377,7 @@ public:
bitmap_yuy16(UINT16 *base, int width, int height, int rowpixels) : bitmap16_t(k_bitmap_format, base, width, height, rowpixels) { }
bitmap_yuy16(bitmap_yuy16 &source, const rectangle &subrect) : bitmap16_t(k_bitmap_format, source, subrect) { }
void wrap(UINT16 *base, int width, int height, int rowpixels) { bitmap_t::wrap(base, width, height, rowpixels); }
void wrap(bitmap_yuy16 &source, const rectangle &subrect) { bitmap_t::wrap(static_cast<bitmap_t &>(source), subrect); }
void wrap(const bitmap_yuy16 &source, const rectangle &subrect) { bitmap_t::wrap(static_cast<const bitmap_t &>(source), subrect); }
// getters
bitmap_format format() const { return k_bitmap_format; }
@ -394,7 +394,7 @@ public:
bitmap_rgb32(UINT32 *base, int width, int height, int rowpixels) : bitmap32_t(k_bitmap_format, base, width, height, rowpixels) { }
bitmap_rgb32(bitmap_rgb32 &source, const rectangle &subrect) : bitmap32_t(k_bitmap_format, source, subrect) { }
void wrap(UINT32 *base, int width, int height, int rowpixels) { bitmap_t::wrap(base, width, height, rowpixels); }
void wrap(bitmap_rgb32 &source, const rectangle &subrect) { bitmap_t::wrap(static_cast<bitmap_t &>(source), subrect); }
void wrap(const bitmap_rgb32 &source, const rectangle &subrect) { bitmap_t::wrap(static_cast<const bitmap_t &>(source), subrect); }
// getters
bitmap_format format() const { return k_bitmap_format; }
@ -411,7 +411,7 @@ public:
bitmap_argb32(UINT32 *base, int width, int height, int rowpixels) : bitmap32_t(k_bitmap_format, base, width, height, rowpixels) { }
bitmap_argb32(bitmap_argb32 &source, const rectangle &subrect) : bitmap32_t(k_bitmap_format, source, subrect) { }
void wrap(UINT32 *base, int width, int height, int rowpixels) { bitmap_t::wrap(base, width, height, rowpixels); }
void wrap(bitmap_argb32 &source, const rectangle &subrect) { bitmap_t::wrap(static_cast<bitmap_t &>(source), subrect); }
void wrap(const bitmap_argb32 &source, const rectangle &subrect) { bitmap_t::wrap(static_cast<const bitmap_t &>(source), subrect); }
// getters
bitmap_format format() const { return k_bitmap_format; }

265
src/lib/util/bitstream.h Normal file
View File

@ -0,0 +1,265 @@
/***************************************************************************
bitstream.h
Helper classes for reading/writing at the bit level.
****************************************************************************
Copyright Aaron Giles
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name 'MAME' nor the names of its contributors may be
used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
***************************************************************************/
#pragma once
#ifndef __BITSTREAM_H__
#define __BITSTREAM_H__
#include "osdcore.h"
//**************************************************************************
// TYPE DEFINITIONS
//**************************************************************************
// helper class for reading from a bit buffer
class bitstream_in
{
public:
// construction/destruction
bitstream_in(const void *src, UINT32 srclength);
// getters
bool overflow() const { return ((m_doffset - m_bits / 8) > m_dlength); }
UINT32 read_offset() const;
// operations
UINT32 read(int numbits);
UINT32 peek(int numbits);
void remove(int numbits);
UINT32 flush();
private:
// internal state
UINT32 m_buffer; // current bit accumulator
int m_bits; // number of bits in the accumulator
const UINT8 * m_read; // read pointer
UINT32 m_doffset; // byte offset within the data
UINT32 m_dlength; // length of the data
};
// helper class for writing to a bit buffer
class bitstream_out
{
public:
// construction/destruction
bitstream_out(void *dest, UINT32 destlength);
// getters
bool overflow() const { return (m_doffset > m_dlength); }
// operations
void write(UINT32 newbits, int numbits);
UINT32 flush();
private:
// internal state
UINT32 m_buffer; // current bit accumulator
int m_bits; // number of bits in the accumulator
UINT8 * m_write; // write pointer
UINT32 m_doffset; // byte offset within the data
UINT32 m_dlength; // length of the data
};
//**************************************************************************
// INLINE FUNCTIONS
//**************************************************************************
//-------------------------------------------------
// bitstream_in - constructor
//-------------------------------------------------
inline bitstream_in::bitstream_in(const void *src, UINT32 srclength)
: m_buffer(NULL),
m_bits(0),
m_read(reinterpret_cast<const UINT8 *>(src)),
m_doffset(0),
m_dlength(srclength)
{
}
//-------------------------------------------------
// peek - fetch the requested number of bits
// but don't advance the input pointer
//-------------------------------------------------
inline UINT32 bitstream_in::peek(int numbits)
{
// fetch data if we need more
if (numbits > m_bits)
{
while (m_bits <= 24)
{
if (m_doffset < m_dlength)
m_buffer |= m_read[m_doffset] << (24 - m_bits);
m_doffset++;
m_bits += 8;
}
}
// return the data
return m_buffer >> (32 - numbits);
}
//-------------------------------------------------
// remove - advance the input pointer by the
// specified number of bits
//-------------------------------------------------
inline void bitstream_in::remove(int numbits)
{
m_buffer <<= numbits;
m_bits -= numbits;
}
//-------------------------------------------------
// read - fetch the requested number of bits
//-------------------------------------------------
inline UINT32 bitstream_in::read(int numbits)
{
UINT32 result = peek(numbits);
remove(numbits);
return result;
}
//-------------------------------------------------
// read_offset - return the current read offset
//-------------------------------------------------
inline UINT32 bitstream_in::read_offset() const
{
UINT32 result = m_doffset;
int bits = m_bits;
while (bits >= 8)
{
result--;
bits -= 8;
}
return result;
}
//-------------------------------------------------
// flush - flush to the nearest byte
//-------------------------------------------------
inline UINT32 bitstream_in::flush()
{
while (m_bits >= 8)
{
m_doffset--;
m_bits -= 8;
}
m_bits = m_buffer = 0;
return m_doffset;
}
//-------------------------------------------------
// bitstream_out - constructor
//-------------------------------------------------
inline bitstream_out::bitstream_out(void *dest, UINT32 destlength)
: m_buffer(0),
m_bits(0),
m_write(reinterpret_cast<UINT8 *>(dest)),
m_doffset(0),
m_dlength(destlength)
{
}
//-------------------------------------------------
// write - write the given number of bits to the
// data stream
//-------------------------------------------------
inline void bitstream_out::write(UINT32 newbits, int numbits)
{
// flush the buffer if we're going to overflow it
if (m_bits + numbits > 32)
while (m_bits >= 8)
{
if (m_doffset < m_dlength)
m_write[m_doffset] = m_buffer >> 24;
m_doffset++;
m_buffer <<= 8;
m_bits -= 8;
}
// shift the bits to the top
newbits <<= 32 - numbits;
// now shift it down to account for the number of bits we already have and OR them in
m_buffer |= newbits >> m_bits;
m_bits += numbits;
}
//-------------------------------------------------
// flush - output remaining bits and return the
// final output size in bytes
//-------------------------------------------------
inline UINT32 bitstream_out::flush()
{
while (m_bits > 0)
{
if (m_doffset < m_dlength)
m_write[m_doffset] = m_buffer >> 24;
m_doffset++;
m_buffer <<= 8;
m_bits -= 8;
}
m_bits = m_buffer = 0;
return m_doffset;
}
#endif

View File

@ -40,7 +40,7 @@
IMPORTANT:
"physical" block addresses are the actual addresses on the emulated CD.
"chd" block addresses are the block addresses in the CHD file.
Because we pad each track to a hunk boundry, these addressing
Because we pad each track to a 4-frame boundry, these addressing
schemes will differ after track 1!
***************************************************************************/
@ -75,22 +75,11 @@ struct _cdrom_file
chd_file * chd; /* CHD file */
cdrom_toc cdtoc; /* TOC for the CD */
chdcd_track_input_info track_info; /* track info */
UINT32 hunksectors; /* sectors per hunk */
UINT32 cachehunk; /* which hunk is cached */
UINT8 * cache; /* cache of the current hunk */
core_file * fhandle[CD_MAX_TRACKS];/* file handle */
};
/***************************************************************************
FUNCTION PROTOTYPES
***************************************************************************/
static chd_error read_sector_into_cache(cdrom_file *file, UINT32 lbasector, UINT32 *sectoroffs, UINT32 *tracknum);
/***************************************************************************
INLINE FUNCTIONS
***************************************************************************/
@ -100,7 +89,7 @@ static chd_error read_sector_into_cache(cdrom_file *file, UINT32 lbasector, UINT
and the track number
-------------------------------------------------*/
INLINE UINT32 physical_to_chd_lba(cdrom_file *file, UINT32 physlba, UINT32 *tracknum)
INLINE UINT32 physical_to_chd_lba(cdrom_file *file, UINT32 physlba, UINT32 &tracknum)
{
UINT32 chdlba;
int track;
@ -110,8 +99,7 @@ INLINE UINT32 physical_to_chd_lba(cdrom_file *file, UINT32 physlba, UINT32 *trac
if (physlba < file->cdtoc.tracks[track + 1].physframeofs)
{
chdlba = physlba - file->cdtoc.tracks[track].physframeofs + file->cdtoc.tracks[track].chdframeofs;
if (tracknum != NULL)
*tracknum = track;
tracknum = track;
return chdlba;
}
@ -136,31 +124,29 @@ cdrom_file *cdrom_open(const char *inputfile)
return NULL;
/* setup the CDROM module and get the disc info */
chd_error err = chdcd_parse_toc(inputfile, &file->cdtoc, &file->track_info);
chd_error err = chdcd_parse_toc(inputfile, file->cdtoc, file->track_info);
if (err != CHDERR_NONE)
{
fprintf(stderr, "Error reading input file: %s\n", chd_error_string(err));
fprintf(stderr, "Error reading input file: %s\n", chd_file::error_string(err));
return NULL;
}
/* fill in the data */
file->chd = NULL;
file->hunksectors = 1;
file->cachehunk = -1;
LOG(("CD has %d tracks\n", file->cdtoc.numtrks));
for (i = 0; i < file->cdtoc.numtrks; i++)
{
file_error filerr = core_fopen(file->track_info.fname[i], OPEN_FLAG_READ, &file->fhandle[i]);
file_error filerr = core_fopen(file->track_info.track[i].fname, OPEN_FLAG_READ, &file->fhandle[i]);
if (filerr != FILERR_NONE)
{
fprintf(stderr, "Unable to open file: %s\n", file->track_info.fname[i]);
fprintf(stderr, "Unable to open file: %s\n", file->track_info.track[i].fname.cstr());
return NULL;
}
}
/* calculate the starting frame for each track, keeping in mind that CHDMAN
pads tracks out with extra frames to fit hunk size boundries
pads tracks out with extra frames to fit 4-frame size boundries
*/
physofs = 0;
for (i = 0; i < file->cdtoc.numtrks; i++)
@ -185,14 +171,6 @@ cdrom_file *cdrom_open(const char *inputfile)
file->cdtoc.tracks[i].physframeofs = physofs;
file->cdtoc.tracks[i].chdframeofs = 0;
/* allocate a cache */
file->cache = (UINT8 *)malloc(CD_FRAME_SIZE);
if (file->cache == NULL)
{
free(file);
return NULL;
}
return file;
}
@ -203,7 +181,6 @@ cdrom_file *cdrom_open(const char *inputfile)
cdrom_file *cdrom_open(chd_file *chd)
{
const chd_header *header = chd_get_header(chd);
int i;
cdrom_file *file;
UINT32 physofs, chdofs;
@ -214,7 +191,9 @@ cdrom_file *cdrom_open(chd_file *chd)
return NULL;
/* validate the CHD information */
if (header->hunkbytes % CD_FRAME_SIZE != 0)
if (chd->hunk_bytes() % CD_FRAME_SIZE != 0)
return NULL;
if (chd->unit_bytes() != CD_FRAME_SIZE)
return NULL;
/* allocate memory for the CD-ROM file */
@ -224,8 +203,6 @@ cdrom_file *cdrom_open(chd_file *chd)
/* fill in the data */
file->chd = chd;
file->hunksectors = header->hunkbytes / CD_FRAME_SIZE;
file->cachehunk = -1;
/* read the CD-ROM metadata */
err = cdrom_parse_metadata(chd, &file->cdtoc);
@ -238,7 +215,7 @@ cdrom_file *cdrom_open(chd_file *chd)
LOG(("CD has %d tracks\n", file->cdtoc.numtrks));
/* calculate the starting frame for each track, keeping in mind that CHDMAN
pads tracks out with extra frames to fit hunk size boundries
pads tracks out with extra frames to fit 4-frame size boundries
*/
physofs = chdofs = 0;
for (i = 0; i < file->cdtoc.numtrks; i++)
@ -265,14 +242,6 @@ cdrom_file *cdrom_open(chd_file *chd)
file->cdtoc.tracks[i].physframeofs = physofs;
file->cdtoc.tracks[i].chdframeofs = chdofs;
/* allocate a cache */
file->cache = (UINT8 *)malloc(chd_get_header(chd)->hunkbytes);
if (file->cache == NULL)
{
free(file);
return NULL;
}
return file;
}
@ -286,10 +255,6 @@ void cdrom_close(cdrom_file *file)
if (file == NULL)
return;
/* free the cache */
if (file->cache)
free(file->cache);
if (file->chd == NULL)
{
for (int i = 0; i < file->cdtoc.numtrks; i++)
@ -307,6 +272,37 @@ void cdrom_close(cdrom_file *file)
CORE READ ACCESS
***************************************************************************/
chd_error read_partial_sector(cdrom_file *file, void *dest, UINT32 chdsector, UINT32 tracknum, UINT32 startoffs, UINT32 length)
{
// if a CHD, just read
if (file->chd != NULL)
return file->chd->read_bytes(UINT64(chdsector) * UINT64(CD_FRAME_SIZE) + startoffs, dest, length);
// else read from the appropriate file
core_file *srcfile = file->fhandle[tracknum];
UINT64 sourcefileoffset = file->track_info.track[tracknum].offset;
int bytespersector = file->cdtoc.tracks[tracknum].datasize + file->cdtoc.tracks[tracknum].subsize;
sourcefileoffset += chdsector * bytespersector + startoffs;
core_fseek(srcfile, sourcefileoffset, SEEK_SET);
core_fread(srcfile, dest, length);
if (file->track_info.track[tracknum].swap)
{
UINT8 *buffer = (UINT8 *)dest - startoffs;
for (int swapindex = startoffs; swapindex < 2352; swapindex += 2 )
{
int swaptemp = buffer[ swapindex ];
buffer[ swapindex ] = buffer[ swapindex + 1 ];
buffer[ swapindex + 1 ] = swaptemp;
}
}
return CHDERR_NONE;
}
/*-------------------------------------------------
cdrom_read_data - read one or more sectors
from a CD-ROM
@ -314,31 +310,25 @@ void cdrom_close(cdrom_file *file)
UINT32 cdrom_read_data(cdrom_file *file, UINT32 lbasector, void *buffer, UINT32 datatype)
{
UINT32 tracktype, tracknum, sectoroffs;
chd_error err;
static const UINT8 syncbytes[12] = {0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00};
if (file == NULL)
return 0;
/* cache in the sector */
err = read_sector_into_cache(file, lbasector, &sectoroffs, &tracknum);
if (err != CHDERR_NONE)
return 0;
// compute CHD sector and tracknumber
UINT32 tracknum = 0;
UINT32 chdsector = physical_to_chd_lba(file, lbasector, tracknum);
/* copy out the requested sector */
tracktype = file->cdtoc.tracks[tracknum].trktype;
UINT32 tracktype = file->cdtoc.tracks[tracknum].trktype;
if ((datatype == tracktype) || (datatype == CD_TRACK_RAW_DONTCARE))
{
memcpy(buffer, &file->cache[sectoroffs * CD_FRAME_SIZE], file->cdtoc.tracks[tracknum].datasize);
return (read_partial_sector(file, buffer, chdsector, tracknum, 0, file->cdtoc.tracks[tracknum].datasize) == CHDERR_NONE);
}
else
{
/* return 2048 bytes of mode 1 data from a 2352 byte mode 1 raw sector */
if ((datatype == CD_TRACK_MODE1) && (tracktype == CD_TRACK_MODE1_RAW))
{
memcpy(buffer, &file->cache[(sectoroffs * CD_FRAME_SIZE) + 16], 2048);
return 1;
return (read_partial_sector(file, buffer, chdsector, tracknum, 16, 2048) == CHDERR_NONE);
}
/* return 2352 byte mode 1 raw sector from 2048 bytes of mode 1 data */
@ -347,34 +337,31 @@ UINT32 cdrom_read_data(cdrom_file *file, UINT32 lbasector, void *buffer, UINT32
UINT8 *bufptr = (UINT8 *)buffer;
UINT32 msf = lba_to_msf(lbasector);
static const UINT8 syncbytes[12] = {0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00};
memcpy(bufptr, syncbytes, 12);
bufptr[12] = msf>>16;
bufptr[13] = msf>>8;
bufptr[14] = msf&0xff;
bufptr[15] = 1; // mode 1
memcpy(bufptr+16, &file->cache[(sectoroffs * CD_FRAME_SIZE)], 2048);
LOG(("CDROM: promotion of mode1/form1 sector to mode1 raw is not complete!\n"));
return 1;
return (read_partial_sector(file, bufptr+16, chdsector, tracknum, 0, 2048) == CHDERR_NONE);
}
/* return 2048 bytes of mode 1 data from a mode2 form1 or raw sector */
if ((datatype == CD_TRACK_MODE1) && ((tracktype == CD_TRACK_MODE2_FORM1)||(tracktype == CD_TRACK_MODE2_RAW)))
{
memcpy(buffer, &file->cache[(sectoroffs * CD_FRAME_SIZE) + 24], 2048);
return 1;
return (read_partial_sector(file, buffer, chdsector, tracknum, 24, 2048) == CHDERR_NONE);
}
/* return mode 2 2336 byte data from a 2352 byte mode 1 or 2 raw sector (skip the header) */
if ((datatype == CD_TRACK_MODE2) && ((tracktype == CD_TRACK_MODE1_RAW) || (tracktype == CD_TRACK_MODE2_RAW)))
{
memcpy(buffer, &file->cache[(sectoroffs * CD_FRAME_SIZE) + 16], 2336);
return 1;
return (read_partial_sector(file, buffer, chdsector, tracknum, 16, 2336) == CHDERR_NONE);
}
LOG(("CDROM: Conversion from type %d to type %d not supported!\n", tracktype, datatype));
return 0;
}
return 1;
}
@ -385,20 +372,18 @@ UINT32 cdrom_read_data(cdrom_file *file, UINT32 lbasector, void *buffer, UINT32
UINT32 cdrom_read_subcode(cdrom_file *file, UINT32 lbasector, void *buffer)
{
UINT32 sectoroffs, tracknum;
chd_error err;
if (file == NULL)
return ~0;
/* cache in the sector */
err = read_sector_into_cache(file, lbasector, &sectoroffs, &tracknum);
if (err != CHDERR_NONE)
return 0;
/* copy out the requested data */
memcpy(buffer, &file->cache[(sectoroffs * CD_FRAME_SIZE) + file->cdtoc.tracks[tracknum].datasize], file->cdtoc.tracks[tracknum].subsize);
return 1;
// compute CHD sector and tracknumber
UINT32 tracknum = 0;
UINT32 chdsector = physical_to_chd_lba(file, lbasector, tracknum);
if (file->cdtoc.tracks[tracknum].subsize == 0)
return 1;
// read the data
chd_error err = read_partial_sector(file, buffer, chdsector, tracknum, file->cdtoc.tracks[tracknum].datasize, file->cdtoc.tracks[tracknum].subsize);
return (err == CHDERR_NONE);
}
@ -420,7 +405,7 @@ UINT32 cdrom_get_track(cdrom_file *file, UINT32 frame)
return ~0;
/* convert to a CHD sector offset and get track information */
physical_to_chd_lba(file, frame, &track);
physical_to_chd_lba(file, frame, track);
return track;
}
@ -703,57 +688,6 @@ const char *cdrom_get_subtype_string(UINT32 subtype)
INTERNAL UTILITIES
***************************************************************************/
/*-------------------------------------------------
read_sector_into_cache - cache a sector at
the given physical LBA
-------------------------------------------------*/
static chd_error read_sector_into_cache(cdrom_file *file, UINT32 lbasector, UINT32 *sectoroffs, UINT32 *tracknum)
{
UINT32 chdsector, hunknum;
chd_error err;
/* convert to a CHD sector offset and get track information */
*tracknum = 0;
chdsector = physical_to_chd_lba(file, lbasector, tracknum);
hunknum = chdsector / file->hunksectors;
*sectoroffs = chdsector % file->hunksectors;
/* if we haven't cached this hunk, read it now */
if (file->cachehunk != hunknum)
{
if (file->chd) {
err = chd_read(file->chd, hunknum, file->cache);
if (err != CHDERR_NONE)
return err;
} else {
core_file *srcfile = file->fhandle[*tracknum];
UINT64 sourcefileoffset = file->track_info.offset[*tracknum];
int bytespersector = file->cdtoc.tracks[*tracknum].datasize + file->cdtoc.tracks[*tracknum].subsize;
sourcefileoffset += chdsector * bytespersector;
core_fseek(srcfile, sourcefileoffset, SEEK_SET);
core_fread(srcfile, file->cache, bytespersector);
if (file->track_info.swap[*tracknum])
{
for (int swapindex = 0; swapindex < 2352; swapindex += 2 )
{
int swaptemp = file->cache[ swapindex ];
file->cache[ swapindex ] = file->cache[ swapindex + 1 ];
file->cache[ swapindex + 1 ] = swaptemp;
}
}
}
file->cachehunk = hunknum;
}
return CHDERR_NONE;
}
/*-------------------------------------------------
cdrom_parse_metadata - parse metadata into the
TOC structure
@ -761,24 +695,21 @@ static chd_error read_sector_into_cache(cdrom_file *file, UINT32 lbasector, UINT
chd_error cdrom_parse_metadata(chd_file *chd, cdrom_toc *toc)
{
static UINT32 oldmetadata[CD_METADATA_WORDS], *mrp;
const chd_header *header = chd_get_header(chd);
UINT32 hunksectors = header->hunkbytes / CD_FRAME_SIZE;
char metadata[512];
astring metadata;
chd_error err;
int i;
/* start with no tracks */
for (toc->numtrks = 0; toc->numtrks < CD_MAX_TRACKS; toc->numtrks++)
{
int tracknum = -1, frames = 0, hunks, pregap, postgap;
int tracknum = -1, frames = 0, pregap, postgap;
char type[16], subtype[16], pgtype[16], pgsub[16];
cdrom_track_info *track;
pregap = postgap = 0;
/* fetch the metadata for this track */
err = chd_get_metadata(chd, CDROM_TRACK_METADATA_TAG, toc->numtrks, metadata, sizeof(metadata), NULL, NULL, NULL);
err = chd->read_metadata(CDROM_TRACK_METADATA_TAG, toc->numtrks, metadata);
if (err == CHDERR_NONE)
{
/* parse the metadata */
@ -791,7 +722,7 @@ chd_error cdrom_parse_metadata(chd_file *chd, cdrom_toc *toc)
}
else
{
err = chd_get_metadata(chd, CDROM_TRACK_METADATA2_TAG, toc->numtrks, metadata, sizeof(metadata), NULL, NULL, NULL);
err = chd->read_metadata(CDROM_TRACK_METADATA2_TAG, toc->numtrks, metadata);
if (err != CHDERR_NONE)
break;
/* parse the metadata */
@ -818,8 +749,8 @@ chd_error cdrom_parse_metadata(chd_file *chd, cdrom_toc *toc)
/* set the frames and extra frames data */
track->frames = frames;
hunks = (frames + hunksectors - 1) / hunksectors;
track->extraframes = hunks * hunksectors - frames;
int padded = (frames + CD_TRACK_PADDING - 1) / CD_TRACK_PADDING;
track->extraframes = padded * CD_TRACK_PADDING - frames;
/* set the pregap info */
track->pregap = pregap;
@ -839,12 +770,13 @@ chd_error cdrom_parse_metadata(chd_file *chd, cdrom_toc *toc)
return CHDERR_NONE;
/* look for old-style metadata */
err = chd_get_metadata(chd, CDROM_OLD_METADATA_TAG, 0, oldmetadata, sizeof(oldmetadata), NULL, NULL, NULL);
dynamic_buffer oldmetadata;
err = chd->read_metadata(CDROM_OLD_METADATA_TAG, 0, oldmetadata);
if (err != CHDERR_NONE)
return err;
/* reconstruct the TOC from it */
mrp = &oldmetadata[0];
UINT32 *mrp = reinterpret_cast<UINT32 *>(&oldmetadata[0]);
toc->numtrks = *mrp++;
for (i = 0; i < CD_MAX_TRACKS; i++)
@ -855,6 +787,12 @@ chd_error cdrom_parse_metadata(chd_file *chd, cdrom_toc *toc)
toc->tracks[i].subsize = *mrp++;
toc->tracks[i].frames = *mrp++;
toc->tracks[i].extraframes = *mrp++;
toc->tracks[i].pregap = 0;
toc->tracks[i].postgap = 0;
toc->tracks[i].pgtype = 0;
toc->tracks[i].pgsub = 0;
toc->tracks[i].pgdatasize = 0;
toc->tracks[i].pgsubsize = 0;
}
/* TODO: I don't know why sometimes the data is one endian and sometimes another */
@ -888,13 +826,13 @@ chd_error cdrom_write_metadata(chd_file *chd, const cdrom_toc *toc)
/* write the metadata */
for (i = 0; i < toc->numtrks; i++)
{
char metadata[512];
sprintf(metadata, CDROM_TRACK_METADATA2_FORMAT, i + 1, cdrom_get_type_string(toc->tracks[i].trktype),
astring metadata;
metadata.format(CDROM_TRACK_METADATA2_FORMAT, i + 1, cdrom_get_type_string(toc->tracks[i].trktype),
cdrom_get_subtype_string(toc->tracks[i].subtype), toc->tracks[i].frames, toc->tracks[i].pregap,
cdrom_get_type_string(toc->tracks[i].pgtype), cdrom_get_subtype_string(toc->tracks[i].pgsub),
toc->tracks[i].postgap);
err = chd_set_metadata(chd, CDROM_TRACK_METADATA2_TAG, i, metadata, strlen(metadata) + 1, CHD_MDFLAGS_CHECKSUM);
err = chd->write_metadata(CDROM_TRACK_METADATA2_TAG, i, metadata);
if (err != CHDERR_NONE)
return err;
}

View File

@ -51,6 +51,9 @@
CONSTANTS
***************************************************************************/
// tracks are padded to a multiple of this many frames
const UINT32 CD_TRACK_PADDING = 4;
#define CD_MAX_TRACKS (99) /* AFAIK the theoretical limit */
#define CD_MAX_SECTOR_DATA (2352)
#define CD_MAX_SUBCODE_DATA (96)
@ -90,8 +93,7 @@ enum
typedef struct _cdrom_file cdrom_file;
typedef struct _cdrom_track_info cdrom_track_info;
struct _cdrom_track_info
struct cdrom_track_info
{
/* fields used by CHDMAN and in MAME */
UINT32 trktype; /* track type */
@ -113,8 +115,7 @@ struct _cdrom_track_info
};
typedef struct _cdrom_toc cdrom_toc;
struct _cdrom_toc
struct cdrom_toc
{
UINT32 numtrks; /* number of tracks */
cdrom_track_info tracks[CD_MAX_TRACKS];

File diff suppressed because it is too large Load Diff

View File

@ -43,16 +43,20 @@
#define __CHD_H__
#include "osdcore.h"
#include "coretmpl.h"
#include "astring.h"
#include "bitmap.h"
#include "corefile.h"
#include "avcomp.h"
#include "hashing.h"
#include "chdcodec.h"
/***************************************************************************
Compressed Hunks of Data header format. All numbers are stored in
Motorola (big-endian) byte ordering. The header is 76 (V1) or 80 (V2)
bytes long.
Motorola (big-endian) byte ordering.
=========================================================================
V1 header:
@ -70,6 +74,21 @@
[ 60] UINT8 parentmd5[16]; // MD5 checksum of parent file
[ 76] (V1 header length)
Flags:
0x00000001 - set if this drive has a parent
0x00000002 - set if this drive allows writes
Compression types:
CHDCOMPRESSION_NONE = 0
CHDCOMPRESSION_ZLIB = 1
V1 map format:
[ 0] UINT64 offset : 44; // starting offset within the file
[ 0] UINT64 length : 20; // length of data; if == hunksize, data is uncompressed
=========================================================================
V2 header:
[ 0] char tag[8]; // 'MComprHD'
@ -86,6 +105,10 @@
[ 60] UINT8 parentmd5[16]; // MD5 checksum of parent file
[ 76] UINT32 seclen; // number of bytes per sector
[ 80] (V2 header length)
Flags and map format are same as V1
=========================================================================
V3 header:
@ -103,6 +126,23 @@
[ 80] UINT8 sha1[20]; // SHA1 checksum of raw data
[100] UINT8 parentsha1[20];// SHA1 checksum of parent file
[120] (V3 header length)
Flags are the same as V1
Compression types:
CHDCOMPRESSION_NONE = 0
CHDCOMPRESSION_ZLIB = 1
CHDCOMPRESSION_ZLIB_PLUS = 2
V3 map format:
[ 0] UINT64 offset; // starting offset within the file
[ 8] UINT32 crc32; // 32-bit CRC of the uncompressed data
[ 12] UINT16 length_lo; // lower 16 bits of length
[ 14] UINT8 length_hi; // upper 8 bits of length
[ 15] UINT8 flags; // flags, indicating compression info
=========================================================================
V4 header:
@ -119,90 +159,117 @@
[ 68] UINT8 parentsha1[20];// combined raw+meta SHA1 of parent
[ 88] UINT8 rawsha1[20]; // raw data SHA1
[108] (V4 header length)
Flags are the same as V1
Compression types:
CHDCOMPRESSION_NONE = 0
CHDCOMPRESSION_ZLIB = 1
CHDCOMPRESSION_ZLIB_PLUS = 2
CHDCOMPRESSION_AV = 3
Map format is the same as V3
=========================================================================
Flags:
0x00000001 - set if this drive has a parent
0x00000002 - set if this drive allows writes
V5 header:
[ 0] char tag[8]; // 'MComprHD'
[ 8] UINT32 length; // length of header (including tag and length fields)
[ 12] UINT32 version; // drive format version
[ 16] UINT32 compressors[4];// which custom compressors are used?
[ 32] UINT64 logicalbytes; // logical size of the data (in bytes)
[ 40] UINT64 mapoffset; // offset to the map
[ 48] UINT64 metaoffset; // offset to the first blob of metadata
[ 56] UINT32 hunkbytes; // number of bytes per hunk (512k maximum)
[ 60] UINT32 unitbytes; // number of bytes per unit within each hunk
[ 64] UINT8 rawsha1[20]; // raw data SHA1
[ 84] UINT8 sha1[20]; // combined raw+meta SHA1
[104] UINT8 parentsha1[20];// combined raw+meta SHA1 of parent
[124] (V5 header length)
If parentsha1 != 0, we have a parent (no need for flags)
If compressors[0] == 0, we are uncompressed (including maps)
V5 uncompressed map format:
[ 0] UINT32 offset; // starting offset / hunk size
V5 compressed map format header:
[ 0] UINT32 length; // length of compressed map
[ 4] UINT48 datastart; // offset of first block
[ 10] UINT16 crc; // crc-16 of the map
[ 12] UINT8 lengthbits; // bits used to encode complength
[ 13] UINT8 hunkbits; // bits used to encode self-refs
[ 14] UINT8 parentunitbits; // bits used to encode parent unit refs
[ 15] UINT8 reserved; // future use
[ 16] (compressed header length)
Each compressed map entry, once expanded, looks like:
[ 0] UINT8 compression; // compression type
[ 1] UINT24 complength; // compressed length
[ 4] UINT48 offset; // offset
[ 10] UINT16 crc; // crc-16 of the data
***************************************************************************/
/***************************************************************************
CONSTANTS
***************************************************************************/
//**************************************************************************
// CONSTANTS
//**************************************************************************
/* header information */
#define CHD_HEADER_VERSION 4
#define CHD_V1_HEADER_SIZE 76
#define CHD_V2_HEADER_SIZE 80
#define CHD_V3_HEADER_SIZE 120
#define CHD_V4_HEADER_SIZE 108
#define CHD_MAX_HEADER_SIZE CHD_V4_HEADER_SIZE
// pseudo-codecs returned by hunk_info
const chd_codec_type CHD_CODEC_SELF = 1; // copy of another hunk
const chd_codec_type CHD_CODEC_PARENT = 2; // copy of a parent's hunk
const chd_codec_type CHD_CODEC_MINI = 3; // legacy "mini" 8-byte repeat
/* checksumming information */
#define CHD_MD5_BYTES 16
#define CHD_SHA1_BYTES 20
// core types
typedef UINT32 chd_metadata_tag;
/* CHD global flags */
#define CHDFLAGS_HAS_PARENT 0x00000001
#define CHDFLAGS_IS_WRITEABLE 0x00000002
#define CHDFLAGS_UNDEFINED 0xfffffffc
// metadata parameters
const chd_metadata_tag CHDMETATAG_WILDCARD = 0;
const UINT32 CHDMETAINDEX_APPEND = ~0;
/* compression types */
#define CHDCOMPRESSION_NONE 0
#define CHDCOMPRESSION_ZLIB 1
#define CHDCOMPRESSION_ZLIB_PLUS 2
#define CHDCOMPRESSION_AV 3
#define CHDCOMPRESSION_ZLIB_PLUS_WITH_FLAC 4
// metadata flags
const UINT8 CHD_MDFLAGS_CHECKSUM = 0x01; // indicates data is checksummed
/* A/V codec configuration parameters */
#define AV_CODEC_COMPRESS_CONFIG 1
#define AV_CODEC_DECOMPRESS_CONFIG 2
// standard hard disk metadata
const chd_metadata_tag HARD_DISK_METADATA_TAG = CHD_MAKE_TAG('G','D','D','D');
extern const char *HARD_DISK_METADATA_FORMAT;
/* metadata parameters */
#define CHDMETATAG_WILDCARD 0
#define CHD_METAINDEX_APPEND ((UINT32)-1)
// hard disk identify information
const chd_metadata_tag HARD_DISK_IDENT_METADATA_TAG = CHD_MAKE_TAG('I','D','N','T');
/* metadata flags */
#define CHD_MDFLAGS_CHECKSUM 0x01 /* indicates data is checksummed */
// hard disk key information
const chd_metadata_tag HARD_DISK_KEY_METADATA_TAG = CHD_MAKE_TAG('K','E','Y',' ');
/* standard hard disk metadata */
#define HARD_DISK_METADATA_TAG 0x47444444 /* 'GDDD' */
#define HARD_DISK_METADATA_FORMAT "CYLS:%d,HEADS:%d,SECS:%d,BPS:%d"
// pcmcia CIS information
const chd_metadata_tag PCMCIA_CIS_METADATA_TAG = CHD_MAKE_TAG('C','I','S',' ');
/* hard disk identify information */
#define HARD_DISK_IDENT_METADATA_TAG 0x49444e54 /* 'IDNT' */
// standard CD-ROM metadata
const chd_metadata_tag CDROM_OLD_METADATA_TAG = CHD_MAKE_TAG('C','H','C','D');
const chd_metadata_tag CDROM_TRACK_METADATA_TAG = CHD_MAKE_TAG('C','H','T','R');
extern const char *CDROM_TRACK_METADATA_FORMAT;
const chd_metadata_tag CDROM_TRACK_METADATA2_TAG = CHD_MAKE_TAG('C','H','T','2');
extern const char *CDROM_TRACK_METADATA2_FORMAT;
/* hard disk key information */
#define HARD_DISK_KEY_METADATA_TAG 0x4b455920 /* 'KEY ' */
// standard A/V metadata
const chd_metadata_tag AV_METADATA_TAG = CHD_MAKE_TAG('A','V','A','V');
extern const char *AV_METADATA_FORMAT;
/* pcmcia CIS information */
#define PCMCIA_CIS_METADATA_TAG 0x43495320 /* 'CIS ' */
// A/V laserdisc frame metadata
const chd_metadata_tag AV_LD_METADATA_TAG = CHD_MAKE_TAG('A','V','L','D');
/* standard CD-ROM metadata */
#define CDROM_OLD_METADATA_TAG 0x43484344 /* 'CHCD' */
#define CDROM_TRACK_METADATA_TAG 0x43485452 /* 'CHTR' */
#define CDROM_TRACK_METADATA_FORMAT "TRACK:%d TYPE:%s SUBTYPE:%s FRAMES:%d"
#define CDROM_TRACK_METADATA2_TAG 0x43485432 /* 'CHT2' */
#define CDROM_TRACK_METADATA2_FORMAT "TRACK:%d TYPE:%s SUBTYPE:%s FRAMES:%d PREGAP:%d PGTYPE:%s PGSUB:%s POSTGAP:%d"
/* standard A/V metadata */
#define AV_METADATA_TAG 0x41564156 /* 'AVAV' */
#define AV_METADATA_FORMAT "FPS:%d.%06d WIDTH:%d HEIGHT:%d INTERLACED:%d CHANNELS:%d SAMPLERATE:%d"
/* A/V laserdisc frame metadata */
#define AV_LD_METADATA_TAG 0x41564C44 /* 'AVLD' */
/* CHD open values */
#define CHD_OPEN_READ 1
#define CHD_OPEN_READWRITE 2
/* error types */
enum _chd_error
// error types
enum chd_error
{
CHDERR_NONE,
CHDERR_NO_INTERFACE,
CHDERR_OUT_OF_MEMORY,
CHDERR_NOT_OPEN,
CHDERR_ALREADY_OPEN,
CHDERR_INVALID_FILE,
CHDERR_INVALID_PARAMETER,
CHDERR_INVALID_DATA,
@ -226,166 +293,303 @@ enum _chd_error
CHDERR_INVALID_METADATA,
CHDERR_INVALID_STATE,
CHDERR_OPERATION_PENDING,
CHDERR_NO_ASYNC_OPERATION,
CHDERR_UNSUPPORTED_FORMAT
CHDERR_UNSUPPORTED_FORMAT,
CHDERR_UNKNOWN_COMPRESSION,
CHDERR_WALKING_PARENT,
CHDERR_COMPRESSING
};
typedef enum _chd_error chd_error;
/***************************************************************************
TYPE DEFINITIONS
***************************************************************************/
//**************************************************************************
// TYPE DEFINITIONS
//**************************************************************************
/* opaque types */
typedef struct _chd_file chd_file;
class chd_codec;
/* extract header structure (NOT the on-disk header structure) */
typedef struct _chd_header chd_header;
struct _chd_header
// ======================> chd_file
// core file class
class chd_file
{
UINT32 length; /* length of header data */
UINT32 version; /* drive format version */
UINT32 flags; /* flags field */
UINT32 compression; /* compression type */
UINT32 hunkbytes; /* number of bytes per hunk */
UINT32 totalhunks; /* total # of hunks represented */
UINT64 logicalbytes; /* logical size of the data */
UINT64 metaoffset; /* offset in file of first metadata */
UINT8 md5[CHD_MD5_BYTES]; /* overall MD5 checksum */
UINT8 parentmd5[CHD_MD5_BYTES]; /* overall MD5 checksum of parent */
UINT8 sha1[CHD_SHA1_BYTES]; /* overall SHA1 checksum */
UINT8 rawsha1[CHD_SHA1_BYTES]; /* SHA1 checksum of raw data */
UINT8 parentsha1[CHD_SHA1_BYTES]; /* overall SHA1 checksum of parent */
friend class chd_file_compressor;
friend class chd_verifier;
UINT32 obsolete_cylinders; /* obsolete field -- do not use! */
UINT32 obsolete_sectors; /* obsolete field -- do not use! */
UINT32 obsolete_heads; /* obsolete field -- do not use! */
UINT32 obsolete_hunksize; /* obsolete field -- do not use! */
// constants
static const UINT32 HEADER_VERSION = 5;
static const UINT32 V3_HEADER_SIZE = 120;
static const UINT32 V4_HEADER_SIZE = 108;
static const UINT32 V5_HEADER_SIZE = 124;
static const UINT32 MAX_HEADER_SIZE = V5_HEADER_SIZE;
public:
// construction/destruction
chd_file();
virtual ~chd_file();
// operators
operator core_file *() { return m_file; }
// getters
bool opened() const { return (m_file != NULL); }
UINT32 version() const { return m_version; }
UINT64 logical_bytes() const { return m_logicalbytes; }
UINT32 hunk_bytes() const { return m_hunkbytes; }
UINT32 hunk_count() const { return m_hunkcount; }
UINT32 unit_bytes() const { return m_unitbytes; }
UINT64 unit_count() const { return m_unitcount; }
bool compressed() const { return (m_compression[0] != CHD_CODEC_NONE); }
chd_codec_type compression(int index) const { return m_compression[index]; }
chd_file *parent() const { return m_parent; }
sha1_t sha1();
sha1_t raw_sha1();
sha1_t parent_sha1();
chd_error hunk_info(UINT32 hunknum, chd_codec_type &compressor, UINT32 &compbytes);
// setters
void set_raw_sha1(sha1_t rawdata);
void set_parent_sha1(sha1_t parent);
// file create
chd_error create(const char *filename, UINT64 logicalbytes, UINT32 hunkbytes, UINT32 unitbytes, chd_codec_type compression[4]);
chd_error create(core_file &file, UINT64 logicalbytes, UINT32 hunkbytes, UINT32 unitbytes, chd_codec_type compression[4]);
chd_error create(const char *filename, UINT64 logicalbytes, UINT32 hunkbytes, chd_codec_type compression[4], chd_file &parent);
chd_error create(core_file &file, UINT64 logicalbytes, UINT32 hunkbytes, chd_codec_type compression[4], chd_file &parent);
// file open
chd_error open(const char *filename, bool writeable = false, chd_file *parent = NULL);
chd_error open(core_file &file, bool writeable = false, chd_file *parent = NULL);
// file close
void close();
// read/write
chd_error read_hunk(UINT32 hunknum, void *buffer);
chd_error write_hunk(UINT32 hunknum, const void *buffer);
chd_error read_units(UINT64 unitnum, void *buffer, UINT32 count = 1);
chd_error write_units(UINT64 unitnum, const void *buffer, UINT32 count = 1);
chd_error read_bytes(UINT64 offset, void *buffer, UINT32 bytes);
chd_error write_bytes(UINT64 offset, const void *buffer, UINT32 bytes);
// metadata management
chd_error read_metadata(chd_metadata_tag searchtag, UINT32 searchindex, astring &output);
chd_error read_metadata(chd_metadata_tag searchtag, UINT32 searchindex, dynamic_buffer &output);
chd_error read_metadata(chd_metadata_tag searchtag, UINT32 searchindex, void *output, UINT32 outputlen, UINT32 &resultlen);
chd_error read_metadata(chd_metadata_tag searchtag, UINT32 searchindex, dynamic_buffer &output, chd_metadata_tag &resulttag, UINT8 &resultflags);
chd_error write_metadata(chd_metadata_tag metatag, UINT32 metaindex, const void *inputbuf, UINT32 inputlen, UINT8 flags = CHD_MDFLAGS_CHECKSUM);
chd_error write_metadata(chd_metadata_tag metatag, UINT32 metaindex, const astring &input, UINT8 flags = CHD_MDFLAGS_CHECKSUM) { return write_metadata(metatag, metaindex, input.cstr(), input.len() + 1, flags = CHD_MDFLAGS_CHECKSUM); }
chd_error write_metadata(chd_metadata_tag metatag, UINT32 metaindex, const dynamic_buffer &input, UINT8 flags = CHD_MDFLAGS_CHECKSUM) { return write_metadata(metatag, metaindex, input, input.count(), flags = CHD_MDFLAGS_CHECKSUM); }
chd_error delete_metadata(chd_metadata_tag metatag, UINT32 metaindex);
chd_error clone_all_metadata(chd_file &source);
// hashing helper
sha1_t compute_overall_sha1(sha1_t rawsha1);
// codec interfaces
chd_error codec_configure(chd_codec_type codec, int param, void *config);
// static helpers
static const char *error_string(chd_error err);
private:
struct metadata_entry;
struct metadata_hash;
// inline helpers
UINT64 be_read(const UINT8 *base, int numbytes);
void be_write(UINT8 *base, UINT64 value, int numbytes);
sha1_t be_read_sha1(const UINT8 *base);
void be_write_sha1(UINT8 *base, sha1_t value);
void file_read(UINT64 offset, void *dest, UINT32 length);
void file_write(UINT64 offset, const void *source, UINT32 length);
UINT64 file_append(const void *source, UINT32 length, UINT32 alignment = 0);
UINT8 bits_for_value(UINT64 value);
// internal helpers
UINT32 guess_unitbytes();
void parse_v3_header(UINT8 *rawheader, sha1_t &parentsha1);
void parse_v4_header(UINT8 *rawheader, sha1_t &parentsha1);
void parse_v5_header(UINT8 *rawheader, sha1_t &parentsha1);
chd_error compress_v5_map();
void decompress_v5_map();
chd_error create_common();
chd_error open_common(bool writeable);
void create_open_common();
void verify_proper_compression_append(UINT32 hunknum);
void hunk_write_compressed(UINT32 hunknum, INT8 compression, const UINT8 *compressed, UINT32 complength, crc16_t crc16);
void hunk_copy_from_self(UINT32 hunknum, UINT32 otherhunk);
void hunk_copy_from_parent(UINT32 hunknum, UINT64 parentunit);
bool metadata_find(chd_metadata_tag metatag, INT32 metaindex, metadata_entry &metaentry, bool resume = false);
void metadata_set_previous_next(UINT64 prevoffset, UINT64 nextoffset);
void metadata_update_hash();
static int CLIB_DECL metadata_hash_compare(const void *elem1, const void *elem2);
// file characteristics
core_file * m_file; // handle to the open core file
bool m_owns_file; // flag indicating if this file should be closed on chd_close()
bool m_allow_reads; // permit reads from this CHD?
bool m_allow_writes; // permit writes to this CHD?
// core parameters from the header
UINT32 m_version; // version of the header
UINT64 m_logicalbytes; // logical size of the raw CHD data in bytes
UINT64 m_mapoffset; // offset of map
UINT64 m_metaoffset; // offset to first metadata bit
UINT32 m_hunkbytes; // size of each raw hunk in bytes
UINT32 m_hunkcount; // number of hunks represented
UINT32 m_unitbytes; // size of each unit in bytes
UINT64 m_unitcount; // number of units represented
chd_codec_type m_compression[4]; // array of compression types used
chd_file * m_parent; // pointer to parent file, or NULL if none
bool m_parent_missing; // are we missing our parent?
// key offsets within the header
UINT64 m_mapoffset_offset; // offset of map offset field
UINT64 m_metaoffset_offset;// offset of metaoffset field
UINT64 m_sha1_offset; // offset of SHA1 field
UINT64 m_rawsha1_offset; // offset of raw SHA1 field
UINT64 m_parentsha1_offset;// offset of paren SHA1 field
// map information
UINT32 m_mapentrybytes; // length of each entry in a map
dynamic_buffer m_rawmap; // raw map data
// compression management
chd_decompressor * m_decompressor[4]; // array of decompression codecs
dynamic_buffer m_compressed; // temporary buffer for compressed data
// caching
dynamic_buffer m_cache; // single-hunk cache for partial reads/writes
UINT32 m_cachehunk; // which hunk is in the cache?
};
/* structure for returning information about a verification pass */
typedef struct _chd_verify_result chd_verify_result;
struct _chd_verify_result
// ======================> chd_file_compressor
// class for creating a new compressed CHD
class chd_file_compressor : public chd_file
{
UINT8 md5[CHD_MD5_BYTES]; /* overall MD5 checksum */
UINT8 sha1[CHD_SHA1_BYTES]; /* overall SHA1 checksum */
UINT8 rawsha1[CHD_SHA1_BYTES]; /* SHA1 checksum of raw data */
UINT8 metasha1[CHD_SHA1_BYTES]; /* SHA1 checksum of metadata */
public:
// construction/destruction
chd_file_compressor();
virtual ~chd_file_compressor();
// compression management
void compress_begin();
chd_error compress_continue(double &progress, double &ratio);
protected:
// required override: read more data
virtual UINT32 read_data(void *dest, UINT64 offset, UINT32 length) = 0;
private:
// hash map for looking up values
class hashmap
{
public:
// construction/destruction
hashmap();
~hashmap();
// operations
void reset();
UINT64 find(crc16_t crc16, sha1_t sha1);
void add(UINT64 itemnum, crc16_t crc16, sha1_t sha1);
// constants
static const UINT64 NOT_FOUND = ~UINT64(0);
private:
// internal entry
struct entry_t
{
entry_t * m_next; // next entry in list
UINT64 m_itemnum; // item number
sha1_t m_sha1; // SHA-1 of the block
};
// block of entries
struct entry_block
{
entry_block(entry_block *prev)
: m_next(prev), m_nextalloc(0) { }
entry_block * m_next; // next block in list
UINT32 m_nextalloc; // next to be allocated
entry_t m_array[16384]; // array of entries
};
// internal state
entry_t * m_map[65536]; // map, hashed by CRC-16
entry_block * m_block_list; // list of allocated blocks
};
// status of a given work item
enum work_status
{
WS_READY = 0,
WS_READING,
WS_QUEUED,
WS_COMPLETE
};
// a CRC-16/SHA-1 pair
struct hash_pair
{
crc16_t m_crc16; // calculated CRC-16
sha1_t m_sha1; // calculated SHA-1
};
// a single work item
struct work_item
{
osd_work_item * m_osd; // OSD work item running on this block
chd_file_compressor *m_compressor; // pointer back to the compressor
volatile work_status m_status; // current status of this item
UINT32 m_hunknum; // number of the hunk we're working on
UINT8 * m_data; // pointer to the data we are working on
UINT8 * m_compressed; // pointer to the compressed data
UINT32 m_complen; // compressed data length
INT8 m_compression; // type of compression used
chd_compressor_group *m_codecs; // codec instance
dynamic_array<hash_pair> m_hash; // array of hashes
};
// internal helpers
static void *async_walk_parent_static(void *param, int threadid);
void async_walk_parent(work_item &item);
static void *async_compress_hunk_static(void *param, int threadid);
void async_compress_hunk(work_item &item, int threadid);
static void *async_read_static(void *param, int threadid);
void async_read();
// current compression status
bool m_walking_parent; // are we building the parent map?
UINT64 m_total_in; // total bytes in
UINT64 m_total_out; // total bytes out
sha1_creator m_compsha1; // running SHA-1 on raw data
// hash lookup maps
hashmap m_parent_map; // hash map for parent
hashmap m_current_map; // hash map for current
// read I/O thread
osd_work_queue * m_read_queue; // work queue for reading
UINT64 m_read_queue_offset;// next offset to enqueue
UINT64 m_read_done_offset; // next offset that will complete
bool m_read_error; // error during reading?
// work item thread
static const int WORK_BUFFER_HUNKS = 256;
osd_work_queue * m_work_queue; // queue for doing work on other threads
dynamic_buffer m_work_buffer; // buffer containing hunk data to work on
dynamic_buffer m_compressed_buffer;// buffer containing compressed data
work_item m_work_item[WORK_BUFFER_HUNKS]; // status of each hunk
chd_compressor_group * m_codecs[WORK_MAX_THREADS]; // codecs to use
// output state
UINT32 m_write_hunk; // next hunk to write
};
/***************************************************************************
FUNCTION PROTOTYPES
***************************************************************************/
/* ----- CHD file management ----- */
/* create a new CHD file fitting the given description */
chd_error chd_create(const char *filename, UINT64 logicalbytes, UINT32 hunkbytes, UINT32 compression, chd_file *parent);
/* same as chd_create(), but accepts an already-opened core_file object */
chd_error chd_create_file(core_file *file, UINT64 logicalbytes, UINT32 hunkbytes, UINT32 compression, chd_file *parent);
/* open an existing CHD file */
chd_error chd_open(const char *filename, int mode, chd_file *parent, chd_file **chd);
/* same as chd_open(), but accepts an already-opened core_file object */
chd_error chd_open_file(core_file *file, int mode, chd_file *parent, chd_file **chd);
/* close a CHD file */
void chd_close(chd_file *chd);
/* return the associated core_file */
core_file *chd_core_file(chd_file *chd);
/* return an error string for the given CHD error */
const char *chd_error_string(chd_error err);
/* ----- CHD header management ----- */
/* return a pointer to the extracted CHD header data */
const chd_header *chd_get_header(chd_file *chd);
/* set a modified header */
chd_error chd_set_header(const char *filename, const chd_header *header);
/* same as chd_set_header(), but accepts an already-opened core_file object */
chd_error chd_set_header_file(core_file *file, const chd_header *header);
/* ----- core data read/write ----- */
/* read one hunk from the CHD file */
chd_error chd_read(chd_file *chd, UINT32 hunknum, void *buffer);
/* read one hunk from the CHD file asynchronously */
chd_error chd_read_async(chd_file *chd, UINT32 hunknum, void *buffer);
/* write one hunk to a CHD file */
chd_error chd_write(chd_file *chd, UINT32 hunknum, const void *buffer);
/* write one hunk to a CHD file asynchronously */
chd_error chd_write_async(chd_file *chd, UINT32 hunknum, const void *buffer);
/* wait for a previously issued async read/write to complete and return the error */
chd_error chd_async_complete(chd_file *chd);
/* ----- metadata management ----- */
/* get indexed metadata of a particular sort */
chd_error chd_get_metadata(chd_file *chd, UINT32 searchtag, UINT32 searchindex, void *output, UINT32 outputlen, UINT32 *resultlen, UINT32 *resulttag, UINT8 *resultflags);
/* set indexed metadata of a particular sort */
chd_error chd_set_metadata(chd_file *chd, UINT32 metatag, UINT32 metaindex, const void *inputbuf, UINT32 inputlen, UINT8 flags);
/* clone all of the metadata from one CHD to another */
chd_error chd_clone_metadata(chd_file *source, chd_file *dest);
/* ----- compression management ----- */
/* begin compressing data to a CHD */
chd_error chd_compress_begin(chd_file *chd);
/* compress the next hunk of data */
chd_error chd_compress_hunk(chd_file *chd, const void *data, double *curratio, int is_half_hunk = 0);
/* finish compressing data to a CHD */
chd_error chd_compress_finish(chd_file *chd, int write_protect);
/* ----- verification management ----- */
/* begin verifying a CHD */
chd_error chd_verify_begin(chd_file *chd);
/* verify a single hunk of data */
chd_error chd_verify_hunk(chd_file *chd);
/* finish verifying a CHD, returning the computed MD5 and SHA1 */
chd_error chd_verify_finish(chd_file *chd, chd_verify_result *result);
/* ----- codec interfaces ----- */
/* set internal codec parameters */
chd_error chd_codec_config(chd_file *chd, int param, void *config);
/* return a string description of a codec */
const char *chd_get_codec_name(UINT32 codec);
#endif /* __CHD_H__ */
#endif // __CHD_H__

View File

@ -304,7 +304,7 @@ UINT64 read_uint64(FILE *infile)
chdcd_parse_toc - parse a CDRWin format CUE file
-------------------------------------------------*/
chd_error chdcd_parse_nero(const char *tocfname, cdrom_toc *outtoc, chdcd_track_input_info *outinfo)
chd_error chdcd_parse_nero(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo)
{
FILE *infile;
unsigned char buffer[12];
@ -322,8 +322,8 @@ chd_error chdcd_parse_nero(const char *tocfname, cdrom_toc *outtoc, chdcd_track_
}
/* clear structures */
memset(outtoc, 0, sizeof(cdrom_toc));
memset(outinfo, 0, sizeof(chdcd_track_input_info));
memset(&outtoc, 0, sizeof(outtoc));
outinfo.reset();
// seek to 12 bytes before the end
fseek(infile, -12, SEEK_END);
@ -372,7 +372,7 @@ chd_error chdcd_parse_nero(const char *tocfname, cdrom_toc *outtoc, chdcd_track_
// printf("TOC type: %08x. Start track %d End track: %d\n", toc_type, start, end);
outtoc->numtrks = (end-start) + 1;
outtoc.numtrks = (end-start) + 1;
offset = 0;
for (track = start; track <= end; track++)
@ -388,27 +388,26 @@ chd_error chdcd_parse_nero(const char *tocfname, cdrom_toc *outtoc, chdcd_track_
index2 = read_uint64(infile);
// printf("Track %d: sector size %d mode %x index0 %llx index1 %llx index2 %llx (pregap %d sectors, length %d sectors)\n", track, size, mode, index0, index1, index2, (UINT32)(index1-index0)/size, (UINT32)(index2-index1)/size);
strncpy(outinfo->fname[track-1], path.cstr(), 256);
strncat(outinfo->fname[track-1], tocfname, 256);
outinfo->offset[track-1] = offset + (UINT32)(index1-index0);
outinfo->idx0offs[track-1] = 0;
outinfo->idx1offs[track-1] = 0;
outinfo.track[track-1].fname.cpy(path.cstr()).cat(tocfname);
outinfo.track[track-1].offset = offset + (UINT32)(index1-index0);
outinfo.track[track-1].idx0offs = 0;
outinfo.track[track-1].idx1offs = 0;
switch (mode>>24)
{
case 0x00: // 2048 byte data
outtoc->tracks[track-1].trktype = CD_TRACK_MODE1;
outinfo->swap[track-1] = 0;
outtoc.tracks[track-1].trktype = CD_TRACK_MODE1;
outinfo.track[track-1].swap = false;
break;
case 0x06: // 2352 byte mode 2 raw
outtoc->tracks[track-1].trktype = CD_TRACK_MODE2_RAW;
outinfo->swap[track-1] = 0;
outtoc.tracks[track-1].trktype = CD_TRACK_MODE2_RAW;
outinfo.track[track-1].swap = false;
break;
case 0x07: // 2352 byte audio
outtoc->tracks[track-1].trktype = CD_TRACK_AUDIO;
outinfo->swap[track-1] = 1;
outtoc.tracks[track-1].trktype = CD_TRACK_AUDIO;
outinfo.track[track-1].swap = true;
break;
default:
@ -416,18 +415,18 @@ chd_error chdcd_parse_nero(const char *tocfname, cdrom_toc *outtoc, chdcd_track_
break;
}
outtoc->tracks[track-1].datasize = size;
outtoc.tracks[track-1].datasize = size;
outtoc->tracks[track-1].subtype = CD_SUB_NONE;
outtoc->tracks[track-1].subsize = 0;
outtoc.tracks[track-1].subtype = CD_SUB_NONE;
outtoc.tracks[track-1].subsize = 0;
outtoc->tracks[track-1].pregap = (UINT32)(index1-index0)/size;
outtoc->tracks[track-1].frames = (UINT32)(index2-index1)/size;
outtoc->tracks[track-1].postgap = 0;
outtoc->tracks[track-1].pgtype = 0;
outtoc->tracks[track-1].pgsub = CD_SUB_NONE;
outtoc->tracks[track-1].pgdatasize = 0;
outtoc->tracks[track-1].pgsubsize = 0;
outtoc.tracks[track-1].pregap = (UINT32)(index1-index0)/size;
outtoc.tracks[track-1].frames = (UINT32)(index2-index1)/size;
outtoc.tracks[track-1].postgap = 0;
outtoc.tracks[track-1].pgtype = 0;
outtoc.tracks[track-1].pgsub = CD_SUB_NONE;
outtoc.tracks[track-1].pgdatasize = 0;
outtoc.tracks[track-1].pgsubsize = 0;
offset += (UINT32)index2-index1;
}
@ -452,7 +451,7 @@ chd_error chdcd_parse_nero(const char *tocfname, cdrom_toc *outtoc, chdcd_track_
chdcd_parse_gdi - parse a Sega GD-ROM rip
-------------------------------------------------*/
static chd_error chdcd_parse_gdi(const char *tocfname, cdrom_toc *outtoc, chdcd_track_input_info *outinfo)
static chd_error chdcd_parse_gdi(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo)
{
FILE *infile;
int i, numtracks;
@ -469,8 +468,8 @@ static chd_error chdcd_parse_gdi(const char *tocfname, cdrom_toc *outtoc, chdcd_
}
/* clear structures */
memset(outtoc, 0, sizeof(cdrom_toc));
memset(outinfo, 0, sizeof(chdcd_track_input_info));
memset(&outtoc, 0, sizeof(outtoc));
outinfo.reset();
fgets(linebuffer,511,infile);
@ -491,16 +490,16 @@ static chd_error chdcd_parse_gdi(const char *tocfname, cdrom_toc *outtoc, chdcd_
trknum=atoi(tok)-1;
outinfo->swap[trknum]=0;
outinfo->offset[trknum]=0;
outinfo.track[trknum].swap=false;
outinfo.track[trknum].offset=0;
//outtoc->tracks[trknum].trktype = CD_TRACK_MODE1;
outtoc->tracks[trknum].datasize = 0;
outtoc->tracks[trknum].subtype = CD_SUB_NONE;
outtoc->tracks[trknum].subsize = 0;
//outtoc.tracks[trknum].trktype = CD_TRACK_MODE1;
outtoc.tracks[trknum].datasize = 0;
outtoc.tracks[trknum].subtype = CD_SUB_NONE;
outtoc.tracks[trknum].subsize = 0;
tok=strtok(NULL," ");
outtoc->tracks[trknum].physframeofs=atoi(tok);
outtoc.tracks[trknum].physframeofs=atoi(tok);
tok=strtok(NULL," ");
trktype=atoi(tok);
@ -510,19 +509,19 @@ static chd_error chdcd_parse_gdi(const char *tocfname, cdrom_toc *outtoc, chdcd_
if(trktype==4 && trksize==2352)
{
outtoc->tracks[trknum].trktype=CD_TRACK_MODE1_RAW;
outtoc->tracks[trknum].datasize=2352;
outtoc.tracks[trknum].trktype=CD_TRACK_MODE1_RAW;
outtoc.tracks[trknum].datasize=2352;
}
if(trktype==4 && trksize==2048)
{
outtoc->tracks[trknum].trktype=CD_TRACK_MODE1;
outtoc->tracks[trknum].datasize=2048;
outtoc.tracks[trknum].trktype=CD_TRACK_MODE1;
outtoc.tracks[trknum].datasize=2048;
}
if(trktype==0)
{
//assert(trksize==2352);
outtoc->tracks[trknum].trktype=CD_TRACK_AUDIO;
outtoc->tracks[trknum].datasize=2352;
outtoc.tracks[trknum].trktype=CD_TRACK_AUDIO;
outtoc.tracks[trknum].datasize=2352;
}
astring name;
@ -539,43 +538,42 @@ static chd_error chdcd_parse_gdi(const char *tocfname, cdrom_toc *outtoc, chdcd_
} while(tok!=NULL && (strrchr(tok,'"')-tok !=(strlen(tok)-1)));
name = name.delchr('"');
}
strncpy(outinfo->fname[trknum], path.cstr(), 256);
strncat(outinfo->fname[trknum], name, 256);
outinfo.track[trknum].fname.cpy(path.cstr()).cat(name);
sz=get_file_size(outinfo->fname[trknum]);
sz=get_file_size(outinfo.track[trknum].fname);
outtoc->tracks[trknum].frames=sz/trksize;
outtoc->tracks[trknum].extraframes=0;
outtoc.tracks[trknum].frames=sz/trksize;
outtoc.tracks[trknum].extraframes=0;
if(trknum!=0)
{
int dif=outtoc->tracks[trknum].physframeofs-(outtoc->tracks[trknum-1].frames+outtoc->tracks[trknum-1].physframeofs);
outtoc->tracks[trknum-1].frames+=dif;
int dif=outtoc.tracks[trknum].physframeofs-(outtoc.tracks[trknum-1].frames+outtoc.tracks[trknum-1].physframeofs);
outtoc.tracks[trknum-1].frames+=dif;
}
/*
if(trknum!=0)
{
outtoc->tracks[trknum-1].extraframes=outtoc->tracks[trknum].physframeofs-(outtoc->tracks[trknum-1].frames+outtoc->tracks[trknum-1].physframeofs);
outtoc.tracks[trknum-1].extraframes=outtoc.tracks[trknum].physframeofs-(outtoc.tracks[trknum-1].frames+outtoc.tracks[trknum-1].physframeofs);
}
*/
hunks = (outtoc->tracks[trknum].frames+CD_FRAMES_PER_HUNK - 1) / CD_FRAMES_PER_HUNK;
outtoc->tracks[trknum].extraframes = hunks * CD_FRAMES_PER_HUNK - outtoc->tracks[trknum].frames;
hunks = (outtoc.tracks[trknum].frames+CD_FRAMES_PER_HUNK - 1) / CD_FRAMES_PER_HUNK;
outtoc.tracks[trknum].extraframes = hunks * CD_FRAMES_PER_HUNK - outtoc.tracks[trknum].frames;
//chdpos+=outtoc->tracks[trknum].frames+outtoc->tracks[trknum].extraframes;
//chdpos+=outtoc.tracks[trknum].frames+outtoc.tracks[trknum].extraframes;
}
/*
for(i=0;i<numtracks;++i)
{
printf("%s %d %d %d\n",outinfo->fname[i],outtoc->tracks[i].frames,outtoc->tracks[i].extraframes,outtoc->tracks[i].physframeofs);
printf("%s %d %d %d\n",outinfo.track[i].fname,outtoc.tracks[i].frames,outtoc.tracks[i].extraframes,outtoc.tracks[i].physframeofs);
}
*/
/* close the input TOC */
fclose(infile);
/* store the number of tracks found */
outtoc->numtrks = numtracks;
outtoc.numtrks = numtracks;
return CHDERR_NONE;
}
@ -584,7 +582,7 @@ static chd_error chdcd_parse_gdi(const char *tocfname, cdrom_toc *outtoc, chdcd_
chdcd_parse_toc - parse a CDRWin format CUE file
-------------------------------------------------*/
chd_error chdcd_parse_cue(const char *tocfname, cdrom_toc *outtoc, chdcd_track_input_info *outinfo)
chd_error chdcd_parse_cue(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo)
{
FILE *infile;
int i, trknum;
@ -601,8 +599,8 @@ chd_error chdcd_parse_cue(const char *tocfname, cdrom_toc *outtoc, chdcd_track_i
}
/* clear structures */
memset(outtoc, 0, sizeof(cdrom_toc));
memset(outinfo, 0, sizeof(chdcd_track_input_info));
memset(&outtoc, 0, sizeof(outtoc));
outinfo.reset();
trknum = -1;
wavoffs = wavlen = 0;
@ -633,11 +631,11 @@ chd_error chdcd_parse_cue(const char *tocfname, cdrom_toc *outtoc, chdcd_track_i
if (!strcmp(token, "BINARY"))
{
outinfo->swap[trknum] = 0;
outinfo.track[trknum].swap = false;
}
else if (!strcmp(token, "MOTOROLA"))
{
outinfo->swap[trknum] = 1;
outinfo.track[trknum].swap = true;
}
else if (!strcmp(token, "WAVE"))
{
@ -672,28 +670,28 @@ chd_error chdcd_parse_cue(const char *tocfname, cdrom_toc *outtoc, chdcd_track_i
if (wavlen != 0)
{
outtoc->tracks[trknum].trktype = CD_TRACK_AUDIO;
outtoc->tracks[trknum].frames = wavlen/2352;
outinfo->offset[trknum] = wavoffs;
outtoc.tracks[trknum].trktype = CD_TRACK_AUDIO;
outtoc.tracks[trknum].frames = wavlen/2352;
outinfo.track[trknum].offset = wavoffs;
wavoffs = wavlen = 0;
}
else
{
outtoc->tracks[trknum].trktype = CD_TRACK_MODE1;
outtoc->tracks[trknum].datasize = 0;
outinfo->offset[trknum] = 0;
outtoc.tracks[trknum].trktype = CD_TRACK_MODE1;
outtoc.tracks[trknum].datasize = 0;
outinfo.track[trknum].offset = 0;
}
outtoc->tracks[trknum].subtype = CD_SUB_NONE;
outtoc->tracks[trknum].subsize = 0;
outtoc->tracks[trknum].pregap = 0;
outinfo->idx0offs[trknum] = -1;
outinfo->idx1offs[trknum] = 0;
outtoc.tracks[trknum].subtype = CD_SUB_NONE;
outtoc.tracks[trknum].subsize = 0;
outtoc.tracks[trknum].pregap = 0;
outinfo.track[trknum].idx0offs = -1;
outinfo.track[trknum].idx1offs = 0;
strncpy(outinfo->fname[trknum], lastfname, 256); // default filename to the last one
// printf("trk %d: fname %s offset %d\n", trknum, &outinfo->fname[trknum][0], outinfo->offset[trknum]);
outinfo.track[trknum].fname.cpy(lastfname); // default filename to the last one
// printf("trk %d: fname %s offset %d\n", trknum, outinfo.track[trknum].fname.cstr(), outinfo.track[trknum].offset);
cdrom_convert_type_string_to_track_info(token, &outtoc->tracks[trknum]);
if (outtoc->tracks[trknum].datasize == 0)
cdrom_convert_type_string_to_track_info(token, &outtoc.tracks[trknum]);
if (outtoc.tracks[trknum].datasize == 0)
{
printf("ERROR: Unknown track type [%s]. Contact MAMEDEV.\n", token);
return CHDERR_FILE_NOT_FOUND;
@ -702,7 +700,7 @@ chd_error chdcd_parse_cue(const char *tocfname, cdrom_toc *outtoc, chdcd_track_i
/* next (optional) token on the line is the subcode type */
TOKENIZE
cdrom_convert_subtype_string_to_track_info(token, &outtoc->tracks[trknum]);
cdrom_convert_subtype_string_to_track_info(token, &outtoc.tracks[trknum]);
}
else if (!strcmp(token, "INDEX")) /* only in bin/cue files */
{
@ -718,14 +716,14 @@ chd_error chdcd_parse_cue(const char *tocfname, cdrom_toc *outtoc, chdcd_track_i
if (idx == 0)
{
outinfo->idx0offs[trknum] = frames;
outinfo.track[trknum].idx0offs = frames;
}
else if (idx == 1)
{
outinfo->idx1offs[trknum] = frames;
if ((outtoc->tracks[trknum].pregap == 0) && (outinfo->idx0offs[trknum] != -1))
outinfo.track[trknum].idx1offs = frames;
if ((outtoc.tracks[trknum].pregap == 0) && (outinfo.track[trknum].idx0offs != -1))
{
outtoc->tracks[trknum].pregap = frames - outinfo->idx0offs[trknum];
outtoc.tracks[trknum].pregap = frames - outinfo.track[trknum].idx0offs;
}
}
}
@ -737,7 +735,7 @@ chd_error chdcd_parse_cue(const char *tocfname, cdrom_toc *outtoc, chdcd_track_i
TOKENIZE
frames = msf_to_frames( token );
outtoc->tracks[trknum].pregap = frames;
outtoc.tracks[trknum].pregap = frames;
}
else if (!strcmp(token, "POSTGAP"))
{
@ -747,7 +745,7 @@ chd_error chdcd_parse_cue(const char *tocfname, cdrom_toc *outtoc, chdcd_track_i
TOKENIZE
frames = msf_to_frames( token );
outtoc->tracks[trknum].postgap = frames;
outtoc.tracks[trknum].postgap = frames;
}
}
}
@ -756,67 +754,67 @@ chd_error chdcd_parse_cue(const char *tocfname, cdrom_toc *outtoc, chdcd_track_i
fclose(infile);
/* store the number of tracks found */
outtoc->numtrks = trknum + 1;
outtoc.numtrks = trknum + 1;
/* now go over the files again and set the lengths */
for (trknum = 0; trknum < outtoc->numtrks; trknum++)
for (trknum = 0; trknum < outtoc.numtrks; trknum++)
{
UINT64 tlen = 0;
// this is true for cue/bin and cue/iso, and we need it for cue/wav since .WAV is little-endian
if (outtoc->tracks[trknum].trktype == CD_TRACK_AUDIO)
if (outtoc.tracks[trknum].trktype == CD_TRACK_AUDIO)
{
outinfo->swap[trknum] = 1;
outinfo.track[trknum].swap = true;
}
// don't do this for .WAV tracks, we already have their length and offset filled out
if (outinfo->offset[trknum] == 0)
if (outinfo.track[trknum].offset == 0)
{
// is this the last track?
if (trknum == (outtoc->numtrks-1))
if (trknum == (outtoc.numtrks-1))
{
/* if we have the same filename as the last track, do it that way */
if (!strcmp(&outinfo->fname[trknum][0], &outinfo->fname[trknum-1][0]))
if (outinfo.track[trknum].fname == outinfo.track[trknum-1].fname)
{
tlen = get_file_size(outinfo->fname[trknum]);
tlen = get_file_size(outinfo.track[trknum].fname);
if (tlen == 0)
{
printf("ERROR: couldn't find bin file [%s]\n", outinfo->fname[trknum-1]);
printf("ERROR: couldn't find bin file [%s]\n", outinfo.track[trknum-1].fname.cstr());
return CHDERR_FILE_NOT_FOUND;
}
outinfo->offset[trknum] = outinfo->offset[trknum-1] + outtoc->tracks[trknum-1].frames * (outtoc->tracks[trknum-1].datasize + outtoc->tracks[trknum-1].subsize);
outtoc->tracks[trknum].frames = (tlen - outinfo->offset[trknum]) / (outtoc->tracks[trknum].datasize + outtoc->tracks[trknum].subsize);
outinfo.track[trknum].offset = outinfo.track[trknum-1].offset + outtoc.tracks[trknum-1].frames * (outtoc.tracks[trknum-1].datasize + outtoc.tracks[trknum-1].subsize);
outtoc.tracks[trknum].frames = (tlen - outinfo.track[trknum].offset) / (outtoc.tracks[trknum].datasize + outtoc.tracks[trknum].subsize);
}
else /* data files are different */
{
tlen = get_file_size(outinfo->fname[trknum]);
tlen = get_file_size(outinfo.track[trknum].fname);
if (tlen == 0)
{
printf("ERROR: couldn't find bin file [%s]\n", outinfo->fname[trknum-1]);
printf("ERROR: couldn't find bin file [%s]\n", outinfo.track[trknum-1].fname.cstr());
return CHDERR_FILE_NOT_FOUND;
}
tlen /= (outtoc->tracks[trknum].datasize + outtoc->tracks[trknum].subsize);
outtoc->tracks[trknum].frames = tlen;
outinfo->offset[trknum] = 0;
tlen /= (outtoc.tracks[trknum].datasize + outtoc.tracks[trknum].subsize);
outtoc.tracks[trknum].frames = tlen;
outinfo.track[trknum].offset = 0;
}
}
else
{
/* if we have the same filename as the next track, do it that way */
if (!strcmp(&outinfo->fname[trknum][0], &outinfo->fname[trknum+1][0]))
if (outinfo.track[trknum].fname == outinfo.track[trknum+1].fname)
{
outtoc->tracks[trknum].frames = outinfo->idx1offs[trknum+1] - outinfo->idx1offs[trknum];
outtoc.tracks[trknum].frames = outinfo.track[trknum+1].idx1offs - outinfo.track[trknum].idx1offs;
if (trknum == 0) // track 0 offset is 0
{
outinfo->offset[trknum] = 0;
outinfo.track[trknum].offset = 0;
}
else
{
outinfo->offset[trknum] = outinfo->offset[trknum-1] + outtoc->tracks[trknum-1].frames * (outtoc->tracks[trknum-1].datasize + outtoc->tracks[trknum-1].subsize);
outinfo.track[trknum].offset = outinfo.track[trknum-1].offset + outtoc.tracks[trknum-1].frames * (outtoc.tracks[trknum-1].datasize + outtoc.tracks[trknum-1].subsize);
}
if (!outtoc->tracks[trknum].frames)
if (!outtoc.tracks[trknum].frames)
{
printf("ERROR: unable to determine size of track %d, missing INDEX 01 markers?\n", trknum+1);
return CHDERR_FILE_NOT_FOUND;
@ -824,19 +822,19 @@ chd_error chdcd_parse_cue(const char *tocfname, cdrom_toc *outtoc, chdcd_track_i
}
else /* data files are different */
{
tlen = get_file_size(outinfo->fname[trknum]);
tlen = get_file_size(outinfo.track[trknum].fname);
if (tlen == 0)
{
printf("ERROR: couldn't find bin file [%s]\n", outinfo->fname[trknum]);
printf("ERROR: couldn't find bin file [%s]\n", outinfo.track[trknum].fname.cstr());
return CHDERR_FILE_NOT_FOUND;
}
tlen /= (outtoc->tracks[trknum].datasize + outtoc->tracks[trknum].subsize);
outtoc->tracks[trknum].frames = tlen;
outinfo->offset[trknum] = 0;
tlen /= (outtoc.tracks[trknum].datasize + outtoc.tracks[trknum].subsize);
outtoc.tracks[trknum].frames = tlen;
outinfo.track[trknum].offset = 0;
}
}
}
printf("trk %d: %d frames @ offset %d\n", trknum+1, outtoc->tracks[trknum].frames, outinfo->offset[trknum]);
printf("trk %d: %d frames @ offset %d\n", trknum+1, outtoc.tracks[trknum].frames, outinfo.track[trknum].offset);
}
return CHDERR_NONE;
@ -846,7 +844,7 @@ chd_error chdcd_parse_cue(const char *tocfname, cdrom_toc *outtoc, chdcd_track_i
chdcd_parse_toc - parse a CDRDAO format TOC file
-------------------------------------------------*/
chd_error chdcd_parse_toc(const char *tocfname, cdrom_toc *outtoc, chdcd_track_input_info *outinfo)
chd_error chdcd_parse_toc(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo)
{
FILE *infile;
int i, trknum;
@ -885,8 +883,8 @@ chd_error chdcd_parse_toc(const char *tocfname, cdrom_toc *outtoc, chdcd_track_i
}
/* clear structures */
memset(outtoc, 0, sizeof(cdrom_toc));
memset(outinfo, 0, sizeof(chdcd_track_input_info));
memset(&outtoc, 0, sizeof(outtoc));
outinfo.reset();
trknum = -1;
@ -910,8 +908,7 @@ chd_error chdcd_parse_toc(const char *tocfname, cdrom_toc *outtoc, chdcd_track_i
TOKENIZE
/* keep the filename */
strncpy(outinfo->fname[trknum], path.cstr(), 256);
strncat(outinfo->fname[trknum], token, 256);
outinfo.track[trknum].fname.cpy(path.cstr()).cat(token);
/* get either the offset or the length */
TOKENIZE
@ -920,11 +917,11 @@ chd_error chdcd_parse_toc(const char *tocfname, cdrom_toc *outtoc, chdcd_track_i
{
TOKENIZE
outinfo->swap[trknum] = 1;
outinfo.track[trknum].swap = true;
}
else
{
outinfo->swap[trknum] = 0;
outinfo.track[trknum].swap = false;
}
if (token[0] == '#')
@ -937,14 +934,14 @@ chd_error chdcd_parse_toc(const char *tocfname, cdrom_toc *outtoc, chdcd_track_i
/* convert the time to an offset */
f = msf_to_frames( token );
f *= (outtoc->tracks[trknum].datasize + outtoc->tracks[trknum].subsize);
f *= (outtoc.tracks[trknum].datasize + outtoc.tracks[trknum].subsize);
}
else
{
f = 0;
}
outinfo->offset[trknum] = f;
outinfo.track[trknum].offset = f;
TOKENIZE
@ -958,19 +955,19 @@ chd_error chdcd_parse_toc(const char *tocfname, cdrom_toc *outtoc, chdcd_track_i
if (isdigit((UINT8)token[0]))
{
// it was an offset.
f *= (outtoc->tracks[trknum].datasize + outtoc->tracks[trknum].subsize);
f *= (outtoc.tracks[trknum].datasize + outtoc.tracks[trknum].subsize);
outinfo->offset[trknum] += f;
outinfo.track[trknum].offset += f;
// this is the length.
f = msf_to_frames( token );
}
}
else if( trknum == 0 && outinfo->offset[trknum] != 0 )
else if( trknum == 0 && outinfo.track[trknum].offset != 0 )
{
/* the 1st track might have a length with no offset */
f = outinfo->offset[trknum] / (outtoc->tracks[trknum].datasize + outtoc->tracks[trknum].subsize);
outinfo->offset[trknum] = 0;
f = outinfo.track[trknum].offset / (outtoc.tracks[trknum].datasize + outtoc.tracks[trknum].subsize);
outinfo.track[trknum].offset = 0;
}
else
{
@ -978,7 +975,7 @@ chd_error chdcd_parse_toc(const char *tocfname, cdrom_toc *outtoc, chdcd_track_i
f = 0;
}
outtoc->tracks[trknum].frames = f;
outtoc.tracks[trknum].frames = f;
}
else if (!strcmp(token, "TRACK"))
{
@ -987,13 +984,13 @@ chd_error chdcd_parse_toc(const char *tocfname, cdrom_toc *outtoc, chdcd_track_i
/* next token on the line is the track type */
TOKENIZE
outtoc->tracks[trknum].trktype = CD_TRACK_MODE1;
outtoc->tracks[trknum].datasize = 0;
outtoc->tracks[trknum].subtype = CD_SUB_NONE;
outtoc->tracks[trknum].subsize = 0;
outtoc.tracks[trknum].trktype = CD_TRACK_MODE1;
outtoc.tracks[trknum].datasize = 0;
outtoc.tracks[trknum].subtype = CD_SUB_NONE;
outtoc.tracks[trknum].subsize = 0;
cdrom_convert_type_string_to_track_info(token, &outtoc->tracks[trknum]);
if (outtoc->tracks[trknum].datasize == 0)
cdrom_convert_type_string_to_track_info(token, &outtoc.tracks[trknum]);
if (outtoc.tracks[trknum].datasize == 0)
{
printf("ERROR: Unknown track type [%s]. Contact MAMEDEV.\n", token);
return CHDERR_FILE_NOT_FOUND;
@ -1002,7 +999,7 @@ chd_error chdcd_parse_toc(const char *tocfname, cdrom_toc *outtoc, chdcd_track_i
/* next (optional) token on the line is the subcode type */
TOKENIZE
cdrom_convert_subtype_string_to_track_info(token, &outtoc->tracks[trknum]);
cdrom_convert_subtype_string_to_track_info(token, &outtoc.tracks[trknum]);
}
else if (!strcmp(token, "START"))
{
@ -1012,7 +1009,7 @@ chd_error chdcd_parse_toc(const char *tocfname, cdrom_toc *outtoc, chdcd_track_i
TOKENIZE
frames = msf_to_frames( token );
outtoc->tracks[trknum].pregap = frames;
outtoc.tracks[trknum].pregap = frames;
}
}
}
@ -1021,7 +1018,7 @@ chd_error chdcd_parse_toc(const char *tocfname, cdrom_toc *outtoc, chdcd_track_i
fclose(infile);
/* store the number of tracks found */
outtoc->numtrks = trknum + 1;
outtoc.numtrks = trknum + 1;
return CHDERR_NONE;
}

View File

@ -14,17 +14,26 @@
#include "cdrom.h"
typedef struct _chdcd_track_input_info chdcd_track_input_info;
struct _chdcd_track_input_info /* used only at compression time */
struct chdcd_track_input_entry
{
char fname[CD_MAX_TRACKS][256]; /* filename for each track */
UINT32 offset[CD_MAX_TRACKS]; /* offset in the data file for each track */
int swap[CD_MAX_TRACKS]; /* data needs to be byte swapped */
UINT32 idx0offs[CD_MAX_TRACKS];
UINT32 idx1offs[CD_MAX_TRACKS];
chdcd_track_input_entry() { reset(); }
void reset() { fname.reset(); offset = idx0offs = idx1offs = 0; swap = false; }
astring fname; // filename for each track
UINT32 offset; // offset in the data file for each track
bool swap; // data needs to be byte swapped
UINT32 idx0offs;
UINT32 idx1offs;
};
struct chdcd_track_input_info
{
void reset() { for (int i = 0; i < CD_MAX_TRACKS; i++) track[i].reset(); }
chdcd_track_input_entry track[CD_MAX_TRACKS];
};
chd_error chdcd_parse_toc(const char *tocfname, cdrom_toc *outtoc, chdcd_track_input_info *outinfo);
chd_error chdcd_parse_toc(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo);
#endif /* __CHDCD_H__ */

1325
src/lib/util/chdcodec.c Normal file

File diff suppressed because it is too large Load Diff

212
src/lib/util/chdcodec.h Normal file
View File

@ -0,0 +1,212 @@
/***************************************************************************
chdcodec.h
Codecs used by the CHD format
****************************************************************************
Copyright Aaron Giles
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name 'MAME' nor the names of its contributors may be
used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
***************************************************************************/
#pragma once
#ifndef __CHDCODEC_H__
#define __CHDCODEC_H__
#include "osdcore.h"
#define CHDCODEC_VERIFY_COMPRESSION 0
//**************************************************************************
// MACROS
//**************************************************************************
#define CHD_MAKE_TAG(a,b,c,d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d))
//**************************************************************************
// TYPE DEFINITIONS
//**************************************************************************
// forward references
class chd_file;
// base types
typedef UINT32 chd_codec_type;
// ======================> chd_codec
// common base class for all compressors and decompressors
class chd_codec
{
protected:
// can't create these directly
chd_codec(chd_file &file, bool lossy);
public:
// allow public deletion
virtual ~chd_codec();
// accessors
chd_file &chd() const { return m_chd; }
bool lossy() const { return m_lossy; }
// implementation
virtual void configure(int param, void *config);
private:
// internal state
chd_file & m_chd;
bool m_lossy;
};
// ======================> chd_compressor
// base class for all compressors
class chd_compressor : public chd_codec
{
protected:
// can't create these directly
chd_compressor(chd_file &file, bool lossy);
public:
// implementation
virtual UINT32 compress(const UINT8 *src, UINT32 srclen, UINT8 *dest) = 0;
};
// ======================> chd_decompressor
// base class for all decompressors
class chd_decompressor : public chd_codec
{
protected:
// can't create these directly
chd_decompressor(chd_file &file, bool lossy);
public:
// implementation
virtual void decompress(const UINT8 *src, UINT32 complen, UINT8 *dest, UINT32 destlen) = 0;
};
// ======================> chd_codec_list
// wrapper to get at the list of codecs
class chd_codec_list
{
public:
// create compressors or decompressors
static chd_compressor *new_compressor(chd_codec_type type, chd_file &file);
static chd_decompressor *new_decompressor(chd_codec_type type, chd_file &file);
// utilities
static bool codec_exists(chd_codec_type type) { return (find_in_list(type) != NULL); }
static const char *codec_name(chd_codec_type type);
private:
// an entry in the list
struct codec_entry
{
chd_codec_type m_type;
bool m_lossy;
const char * m_name;
chd_compressor * (*m_construct_compressor)(chd_file &, bool);
chd_decompressor * (*m_construct_decompressor)(chd_file &, bool);
};
// internal helper functions
static const codec_entry *find_in_list(chd_codec_type type);
template<class _CompressorClass>
static chd_compressor *construct_compressor(chd_file &chd, bool lossy) { return new _CompressorClass(chd, lossy); }
template<class _DecompressorClass>
static chd_decompressor *construct_decompressor(chd_file &chd, bool lossy) { return new _DecompressorClass(chd, lossy); }
// the static list
static const codec_entry s_codec_list[];
};
// ======================> chd_compressor_group
// helper class that wraps several compressors
class chd_compressor_group
{
public:
// construction/destruction
chd_compressor_group(chd_file &file, chd_codec_type compressor_list[4]);
~chd_compressor_group();
// find the best compressor
INT8 find_best_compressor(const UINT8 *src, UINT8 *compressed, UINT32 &complen);
private:
// internal state
UINT32 m_hunkbytes; // number of bytes in a hunk
chd_compressor * m_compressor[4]; // array of active codecs
dynamic_buffer m_compress_test; // test buffer for compression
#if CHDCODEC_VERIFY_COMPRESSION
chd_decompressor * m_decompressor[4]; // array of active codecs
dynamic_buffer m_decompressed; // verification buffer
#endif
};
//**************************************************************************
// CONSTANTS
//**************************************************************************
// currently-defined codecs
const chd_codec_type CHD_CODEC_NONE = 0;
const chd_codec_type CHD_CODEC_ZLIB = CHD_MAKE_TAG('z','l','i','b');
const chd_codec_type CHD_CODEC_LZMA = CHD_MAKE_TAG('l','z','m','a');
const chd_codec_type CHD_CODEC_HUFFMAN = CHD_MAKE_TAG('h','u','f','f');
const chd_codec_type CHD_CODEC_FLAC_BE = CHD_MAKE_TAG('f','l','c','b');
const chd_codec_type CHD_CODEC_FLAC_LE = CHD_MAKE_TAG('f','l','c','l');
const chd_codec_type CHD_CODEC_AVHUFF = CHD_MAKE_TAG('a','v','h','u');
// A/V codec configuration parameters
enum
{
AVHUFF_CODEC_DECOMPRESS_CONFIG = 1
};
#endif // __CHDCODEC_H__

View File

@ -757,6 +757,41 @@ file_error core_fload(const char *filename, void **data, UINT32 *length)
return FILERR_NONE;
}
file_error core_fload(const char *filename, dynamic_buffer &data)
{
core_file *file = NULL;
file_error err;
UINT64 size;
/* attempt to open the file */
err = core_fopen(filename, OPEN_FLAG_READ, &file);
if (err != FILERR_NONE)
return err;
/* get the size */
size = core_fsize(file);
if ((UINT32)size != size)
{
core_fclose(file);
return FILERR_OUT_OF_MEMORY;
}
/* allocate memory */
data.resize(size);
/* read the data */
if (core_fread(file, data, size) != size)
{
core_fclose(file);
data.reset();
return FILERR_FAILURE;
}
/* close the file and return data */
core_fclose(file);
return FILERR_NONE;
}
/***************************************************************************

View File

@ -45,6 +45,7 @@
#include <stdarg.h>
#include "osdcore.h"
#include "astring.h"
#include "coretmpl.h"
@ -129,6 +130,7 @@ const void *core_fbuffer(core_file *file);
/* open a file with the specified filename, read it into memory, and return a pointer */
file_error core_fload(const char *filename, void **data, UINT32 *length);
file_error core_fload(const char *filename, dynamic_buffer &data);

109
src/lib/util/coretmpl.h Normal file
View File

@ -0,0 +1,109 @@
/***************************************************************************
coretmpl.h
Core templates for basic non-string types.
****************************************************************************
Copyright Aaron Giles
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name 'MAME' nor the names of its contributors may be
used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
***************************************************************************/
#pragma once
#ifndef __CORETMPL_H__
#define __CORETMPL_H__
#include <assert.h>
#include "osdcore.h"
// ======================> dynamic_array
// an array that is dynamically sized and can optionally auto-expand
template<class _ElementType>
class dynamic_array
{
private:
// we don't support deep copying
dynamic_array(const dynamic_array &);
dynamic_array &operator=(const dynamic_array &);
public:
// construction/destruction
dynamic_array(int initial = 0)
: m_array(NULL),
m_count(0),
m_allocated(0) { if (initial != 0) expand_internal(initial); m_count = initial; }
virtual ~dynamic_array() { reset(); }
// operators
operator _ElementType *() { return &m_array[0]; }
operator const _ElementType *() const { return &m_array[0]; }
_ElementType operator[](int index) const { assert(index < m_count); return m_array[index]; }
_ElementType &operator[](int index) { assert(index < m_count); return m_array[index]; }
// simple getters
int count() const { return m_count; }
// helpers
void append(const _ElementType &element) { if (m_count == m_allocated) expand_internal((m_allocated == 0) ? 16 : (m_allocated << 1), true); m_array[m_count++] = element; }
void reset() { delete[] m_array; m_array = NULL; m_count = m_allocated = 0; }
void resize(int count, bool keepdata = false) { if (count > m_allocated) expand_internal(count, keepdata); m_count = count; }
private:
// internal helpers
void expand_internal(int count, bool keepdata = true)
{
// allocate a new array, copy the old one, and proceed
m_allocated = count;
_ElementType *newarray = new _ElementType[m_allocated];
if (keepdata)
for (int index = 0; index < m_count; index++)
newarray[index] = m_array[index];
delete[] m_array;
m_array = newarray;
}
// internal state
_ElementType * m_array; // allocated array
int m_count; // number of objects accessed in the list
int m_allocated; // amount of space allocated for the array
};
// ======================> dynamic_buffer
typedef dynamic_array<UINT8> dynamic_buffer;
#endif

598
src/lib/util/flac.c Normal file
View File

@ -0,0 +1,598 @@
/***************************************************************************
flac.c
FLAC compression wrappers
****************************************************************************
Copyright Aaron Giles
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name 'MAME' nor the names of its contributors may be
used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
***************************************************************************/
#include "flac.h"
#include <assert.h>
#include <new>
//**************************************************************************
// FLAC ENCODER
//**************************************************************************
//-------------------------------------------------
// flac_encoder - constructors
//-------------------------------------------------
flac_encoder::flac_encoder()
{
init_common();
}
flac_encoder::flac_encoder(void *buffer, UINT32 buflength)
{
init_common();
reset(buffer, buflength);
}
flac_encoder::flac_encoder(core_file &file)
{
init_common();
reset(file);
}
//-------------------------------------------------
// ~flac_encoder - destructor
//-------------------------------------------------
flac_encoder::~flac_encoder()
{
// delete the encoder
FLAC__stream_encoder_delete(m_encoder);
}
//-------------------------------------------------
// reset - reset state with the original
// parameters
//-------------------------------------------------
bool flac_encoder::reset()
{
// configure the output
m_compressed_offset = 0;
m_ignore_bytes = m_strip_metadata ? 4 : 0;
m_found_audio = !m_strip_metadata;
// configure the encoder in a standard way
// note we do this on each reset; if we don't, results are NOT consistent!
FLAC__stream_encoder_set_verify(m_encoder, false);
// FLAC__stream_encoder_set_do_md5(m_encoder, false);
FLAC__stream_encoder_set_compression_level(m_encoder, 8);
FLAC__stream_encoder_set_channels(m_encoder, m_channels);
FLAC__stream_encoder_set_bits_per_sample(m_encoder, 16);
FLAC__stream_encoder_set_sample_rate(m_encoder, m_sample_rate);
FLAC__stream_encoder_set_total_samples_estimate(m_encoder, 0);
FLAC__stream_encoder_set_streamable_subset(m_encoder, false);
FLAC__stream_encoder_set_blocksize(m_encoder, m_block_size);
// re-start processing
return (FLAC__stream_encoder_init_stream(m_encoder, write_callback_static, NULL, NULL, NULL, this) == FLAC__STREAM_ENCODER_INIT_STATUS_OK);
}
//-------------------------------------------------
// reset - reset state with new memory parameters
//-------------------------------------------------
bool flac_encoder::reset(void *buffer, UINT32 buflength)
{
// configure the output
m_compressed_start = reinterpret_cast<FLAC__byte *>(buffer);
m_compressed_length = buflength;
m_file = NULL;
return reset();
}
//-------------------------------------------------
// reset - reset state with new file parameters
//-------------------------------------------------
bool flac_encoder::reset(core_file &file)
{
// configure the output
m_compressed_start = NULL;
m_compressed_length = 0;
m_file = &file;
return reset();
}
//-------------------------------------------------
// encode_interleaved - encode a buffer with
// interleaved samples
//-------------------------------------------------
bool flac_encoder::encode_interleaved(const INT16 *samples, UINT32 samples_per_channel, bool swap_endian)
{
int shift = swap_endian ? 8 : 0;
// loop over source samples
int num_channels = FLAC__stream_encoder_get_channels(m_encoder);
UINT32 srcindex = 0;
while (samples_per_channel != 0)
{
// process in batches of 2k samples
FLAC__int32 converted_buffer[2048];
FLAC__int32 *dest = converted_buffer;
UINT32 cur_samples = MIN(ARRAY_LENGTH(converted_buffer) / num_channels, samples_per_channel);
// convert a buffers' worth
for (UINT32 sampnum = 0; sampnum < cur_samples; sampnum++)
for (int channel = 0; channel < num_channels; channel++, srcindex++)
*dest++ = INT16((UINT16(samples[srcindex]) << shift) | (UINT16(samples[srcindex]) >> shift));
// process this batch
if (!FLAC__stream_encoder_process_interleaved(m_encoder, converted_buffer, cur_samples))
return false;
samples_per_channel -= cur_samples;
}
return true;
}
//-------------------------------------------------
// encode - encode a buffer with individual
// sample streams
//-------------------------------------------------
bool flac_encoder::encode(INT16 *const *samples, UINT32 samples_per_channel, bool swap_endian)
{
int shift = swap_endian ? 8 : 0;
// loop over source samples
int num_channels = FLAC__stream_encoder_get_channels(m_encoder);
UINT32 srcindex = 0;
while (samples_per_channel != 0)
{
// process in batches of 2k samples
FLAC__int32 converted_buffer[2048];
FLAC__int32 *dest = converted_buffer;
UINT32 cur_samples = MIN(ARRAY_LENGTH(converted_buffer) / num_channels, samples_per_channel);
// convert a buffers' worth
for (UINT32 sampnum = 0; sampnum < cur_samples; sampnum++, srcindex++)
for (int channel = 0; channel < num_channels; channel++)
*dest++ = INT16((UINT16(samples[channel][srcindex]) << shift) | (UINT16(samples[channel][srcindex]) >> shift));
// process this batch
if (!FLAC__stream_encoder_process_interleaved(m_encoder, converted_buffer, cur_samples))
return false;
samples_per_channel -= cur_samples;
}
return true;
}
//-------------------------------------------------
// finish - complete encoding and flush the
// stream
//-------------------------------------------------
UINT32 flac_encoder::finish()
{
// process the data and return the amount written
FLAC__stream_encoder_finish(m_encoder);
return (m_file != NULL) ? core_ftell(m_file) : m_compressed_offset;
}
//-------------------------------------------------
// init_common - common initialization
//-------------------------------------------------
void flac_encoder::init_common()
{
// allocate the encoder
m_encoder = FLAC__stream_encoder_new();
if (m_encoder == NULL)
throw std::bad_alloc();
// initialize default state
m_file = NULL;
m_compressed_offset = 0;
m_compressed_start = NULL;
m_compressed_length = 0;
m_sample_rate = 44100;
m_channels = 2;
m_block_size = 0;
m_strip_metadata = false;
m_ignore_bytes = 0;
m_found_audio = false;
}
//-------------------------------------------------
// write_callback - handle writes to the
// output stream
//-------------------------------------------------
FLAC__StreamEncoderWriteStatus flac_encoder::write_callback_static(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], size_t bytes, unsigned samples, unsigned current_frame, void *client_data)
{
return reinterpret_cast<flac_encoder *>(client_data)->write_callback(buffer, bytes, samples, current_frame);
}
FLAC__StreamEncoderWriteStatus flac_encoder::write_callback(const FLAC__byte buffer[], size_t bytes, unsigned samples, unsigned current_frame)
{
// loop over output data
size_t offset = 0;
while (offset < bytes)
{
// if we're ignoring, continue to do so
if (m_ignore_bytes != 0)
{
int ignore = MIN(bytes - offset, m_ignore_bytes);
offset += ignore;
m_ignore_bytes -= ignore;
}
// if we haven't hit the end of metadata, process a new piece
else if (!m_found_audio)
{
assert(bytes - offset >= 4);
m_found_audio = ((buffer[offset] & 0x80) != 0);
m_ignore_bytes = (buffer[offset + 1] << 16) | (buffer[offset + 2] << 8) | buffer[offset + 3];
offset += 4;
}
// otherwise process as audio data and copy to the output
else
{
int count = bytes - offset;
if (m_file != NULL)
core_fwrite(m_file, buffer, count);
else
{
if (m_compressed_offset + count <= m_compressed_length)
memcpy(m_compressed_start + m_compressed_offset, buffer, count);
m_compressed_offset += count;
}
offset += count;
}
}
return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
}
//**************************************************************************
// FLAC DECODER
//**************************************************************************
//-------------------------------------------------
// flac_decoder - constructor
//-------------------------------------------------
flac_decoder::flac_decoder()
: m_decoder(FLAC__stream_decoder_new()),
m_file(NULL),
m_compressed_offset(0),
m_compressed_start(NULL),
m_compressed_length(0),
m_compressed2_start(NULL),
m_compressed2_length(0)
{
}
//-------------------------------------------------
// flac_decoder - constructor
//-------------------------------------------------
flac_decoder::flac_decoder(const void *buffer, UINT32 length, const void *buffer2, UINT32 length2)
: m_decoder(FLAC__stream_decoder_new()),
m_file(NULL),
m_compressed_offset(0),
m_compressed_start(reinterpret_cast<const FLAC__byte *>(buffer)),
m_compressed_length(length),
m_compressed2_start(reinterpret_cast<const FLAC__byte *>(buffer2)),
m_compressed2_length(length2)
{
reset();
}
//-------------------------------------------------
// flac_decoder - constructor
//-------------------------------------------------
flac_decoder::flac_decoder(core_file &file)
: m_decoder(FLAC__stream_decoder_new()),
m_file(&file),
m_compressed_offset(0),
m_compressed_start(NULL),
m_compressed_length(0),
m_compressed2_start(NULL),
m_compressed2_length(0)
{
reset();
}
//-------------------------------------------------
// flac_decoder - destructor
//-------------------------------------------------
flac_decoder::~flac_decoder()
{
FLAC__stream_decoder_delete(m_decoder);
}
//-------------------------------------------------
// reset - reset state with the original
// parameters
//-------------------------------------------------
bool flac_decoder::reset()
{
m_compressed_offset = 0;
if (FLAC__stream_decoder_init_stream(m_decoder, &flac_decoder::read_callback_static, NULL, NULL, NULL, NULL, &flac_decoder::write_callback_static, NULL, &flac_decoder::error_callback_static, this) != FLAC__STREAM_DECODER_INIT_STATUS_OK)
return false;
return FLAC__stream_decoder_process_until_end_of_metadata(m_decoder);
}
//-------------------------------------------------
// reset - reset state with new memory parameters
//-------------------------------------------------
bool flac_decoder::reset(const void *buffer, UINT32 length, const void *buffer2, UINT32 length2)
{
m_file = NULL;
m_compressed_start = reinterpret_cast<const FLAC__byte *>(buffer);
m_compressed_length = length;
m_compressed2_start = reinterpret_cast<const FLAC__byte *>(buffer2);
m_compressed2_length = length2;
return reset();
}
//-------------------------------------------------
// reset - reset state with new memory parameters
// and a custom-generated header
//-------------------------------------------------
bool flac_decoder::reset(UINT32 sample_rate, UINT8 num_channels, UINT32 block_size, const void *buffer, UINT32 length)
{
// modify the template header with our parameters
static const UINT8 s_header_template[0x2a] =
{
0x66, 0x4C, 0x61, 0x43, // +00: 'fLaC' stream header
0x80, // +04: metadata block type 0 (STREAMINFO),
// flagged as last block
0x00, 0x00, 0x22, // +05: metadata block length = 0x22
0x00, 0x00, // +08: minimum block size
0x00, 0x00, // +0A: maximum block size
0x00, 0x00, 0x00, // +0C: minimum frame size (0 == unknown)
0x00, 0x00, 0x00, // +0F: maximum frame size (0 == unknown)
0x0A, 0xC4, 0x42, 0xF0, 0x00, 0x00, 0x00, 0x00, // +12: sample rate (0x0ac44 == 44100),
// numchannels (2), sample bits (16),
// samples in stream (0 == unknown)
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // +1A: MD5 signature (0 == none)
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 //
// +2A: start of stream data
};
memcpy(m_custom_header, s_header_template, sizeof(s_header_template));
m_custom_header[0x08] = m_custom_header[0x0a] = block_size >> 8;
m_custom_header[0x09] = m_custom_header[0x0b] = block_size & 0xff;
m_custom_header[0x12] = sample_rate >> 12;
m_custom_header[0x13] = sample_rate >> 4;
m_custom_header[0x14] = (sample_rate << 4) | ((num_channels - 1) << 1);
// configure the header ahead of the provided buffer
m_file = NULL;
m_compressed_start = reinterpret_cast<const FLAC__byte *>(m_custom_header);
m_compressed_length = sizeof(m_custom_header);
m_compressed2_start = reinterpret_cast<const FLAC__byte *>(buffer);
m_compressed2_length = length;
return reset();
}
//-------------------------------------------------
// reset - reset state with new file parameter
//-------------------------------------------------
bool flac_decoder::reset(core_file &file)
{
m_file = &file;
m_compressed_start = NULL;
m_compressed_length = 0;
m_compressed2_start = NULL;
m_compressed2_length = 0;
return reset();
}
//-------------------------------------------------
// decode_interleaved - decode to an interleaved
// sound stream
//-------------------------------------------------
bool flac_decoder::decode_interleaved(INT16 *samples, UINT32 num_samples, bool swap_endian)
{
// configure the uncompressed buffer
memset(m_uncompressed_start, 0, sizeof(m_uncompressed_start));
m_uncompressed_start[0] = samples;
m_uncompressed_offset = 0;
m_uncompressed_length = num_samples;
m_uncompressed_swap = swap_endian;
// loop until we get everything we want
while (m_uncompressed_offset < m_uncompressed_length)
if (!FLAC__stream_decoder_process_single(m_decoder))
return false;
return true;
}
//-------------------------------------------------
// decode - decode to an multiple independent
// data streams
//-------------------------------------------------
bool flac_decoder::decode(INT16 **samples, UINT32 num_samples, bool swap_endian)
{
// make sure we don't have too many channels
int chans = channels();
if (chans > ARRAY_LENGTH(m_uncompressed_start))
return false;
// configure the uncompressed buffer
memset(m_uncompressed_start, 0, sizeof(m_uncompressed_start));
for (int curchan = 0; curchan < chans; curchan++)
m_uncompressed_start[curchan] = samples[curchan];
m_uncompressed_offset = 0;
m_uncompressed_length = num_samples;
m_uncompressed_swap = swap_endian;
// loop until we get everything we want
while (m_uncompressed_offset < m_uncompressed_length)
if (!FLAC__stream_decoder_process_single(m_decoder))
return false;
return true;
}
//-------------------------------------------------
// finish - finish up the decode
//-------------------------------------------------
void flac_decoder::finish()
{
FLAC__stream_decoder_finish(m_decoder);
}
//-------------------------------------------------
// read_callback - handle reads from the input
// stream
//-------------------------------------------------
FLAC__StreamDecoderReadStatus flac_decoder::read_callback_static(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data)
{
return reinterpret_cast<flac_decoder *>(client_data)->read_callback(buffer, bytes);
}
FLAC__StreamDecoderReadStatus flac_decoder::read_callback(FLAC__byte buffer[], size_t *bytes)
{
UINT32 expected = *bytes;
// if a file, just read
if (m_file != NULL)
*bytes = core_fread(m_file, buffer, expected);
// otherwise, copy from memory
else
{
// copy from primary buffer first
UINT32 outputpos = 0;
if (outputpos < *bytes && m_compressed_offset < m_compressed_length)
{
UINT32 bytes_to_copy = MIN(*bytes - outputpos, m_compressed_length - m_compressed_offset);
memcpy(&buffer[outputpos], m_compressed_start + m_compressed_offset, bytes_to_copy);
outputpos += bytes_to_copy;
m_compressed_offset += bytes_to_copy;
}
// once we're out of that, copy from the secondary buffer
if (outputpos < *bytes && m_compressed_offset < m_compressed_length + m_compressed2_length)
{
UINT32 bytes_to_copy = MIN(*bytes - outputpos, m_compressed2_length - (m_compressed_offset - m_compressed_length));
memcpy(&buffer[outputpos], m_compressed2_start + m_compressed_offset - m_compressed_length, bytes_to_copy);
outputpos += bytes_to_copy;
m_compressed_offset += bytes_to_copy;
}
*bytes = outputpos;
}
// return based on whether we ran out of data
return (*bytes < expected) ? FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM : FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
}
//-------------------------------------------------
// write_callback - handle writes to the output
// stream
//-------------------------------------------------
FLAC__StreamDecoderWriteStatus flac_decoder::write_callback_static(const FLAC__StreamDecoder *decoder, const ::FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data)
{
return reinterpret_cast<flac_decoder *>(client_data)->write_callback(frame, buffer);
}
FLAC__StreamDecoderWriteStatus flac_decoder::write_callback(const ::FLAC__Frame *frame, const FLAC__int32 * const buffer[])
{
assert(frame->header.channels == channels());
// interleaved case
int shift = m_uncompressed_swap ? 8 : 0;
int blocksize = frame->header.blocksize;
if (m_uncompressed_start[1] == NULL)
{
INT16 *dest = m_uncompressed_start[0] + m_uncompressed_offset * frame->header.channels;
for (int sampnum = 0; sampnum < blocksize && m_uncompressed_offset < m_uncompressed_length; sampnum++, m_uncompressed_offset++)
for (int chan = 0; chan < frame->header.channels; chan++)
*dest++ = INT16((UINT16(buffer[chan][sampnum]) << shift) | (UINT16(buffer[chan][sampnum]) >> shift));
}
// non-interleaved case
else
{
for (int sampnum = 0; sampnum < blocksize && m_uncompressed_offset < m_uncompressed_length; sampnum++, m_uncompressed_offset++)
for (int chan = 0; chan < frame->header.channels; chan++)
if (m_uncompressed_start[chan] != NULL)
m_uncompressed_start[chan][m_uncompressed_offset] = INT16((UINT16(buffer[chan][sampnum]) << shift) | (UINT16(buffer[chan][sampnum]) >> shift));
}
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}
//-------------------------------------------------
// error_callback - handle errors (ignore them)
//-------------------------------------------------
void flac_decoder::error_callback_static(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
{
}

167
src/lib/util/flac.h Normal file
View File

@ -0,0 +1,167 @@
/***************************************************************************
flac.h
FLAC compression wrappers
****************************************************************************
Copyright Aaron Giles
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name 'MAME' nor the names of its contributors may be
used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
***************************************************************************/
#pragma once
#ifndef __FLAC_H__
#define __FLAC_H__
#include "osdcore.h"
#include "corefile.h"
#include "../../lib/libflac/include/flac/all.h"
//**************************************************************************
// TYPE DEFINITIONS
//**************************************************************************
// ======================> flac_encoder
class flac_encoder
{
public:
// construction/destruction
flac_encoder();
flac_encoder(void *buffer, UINT32 buflength);
flac_encoder(core_file &file);
~flac_encoder();
// configuration
void set_sample_rate(UINT32 sample_rate) { m_sample_rate = sample_rate; }
void set_num_channels(UINT8 num_channels) { m_channels = num_channels; }
void set_block_size(UINT32 block_size) { m_block_size = block_size; }
void set_strip_metadata(bool strip) { m_strip_metadata = strip; }
// getters (valid after reset)
FLAC__StreamEncoderState state() const { return FLAC__stream_encoder_get_state(m_encoder); }
const char *state_string() const { return FLAC__stream_encoder_get_resolved_state_string(m_encoder); }
// reset
bool reset();
bool reset(void *buffer, UINT32 buflength);
bool reset(core_file &file);
// encode a buffer
bool encode_interleaved(const INT16 *samples, UINT32 samples_per_channel, bool swap_endian = false);
bool encode(INT16 *const *samples, UINT32 samples_per_channel, bool swap_endian = false);
// finish up
UINT32 finish();
private:
// internal helpers
void init_common();
static FLAC__StreamEncoderWriteStatus write_callback_static(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], size_t bytes, unsigned samples, unsigned current_frame, void *client_data);
FLAC__StreamEncoderWriteStatus write_callback(const FLAC__byte buffer[], size_t bytes, unsigned samples, unsigned current_frame);
// internal state
FLAC__StreamEncoder * m_encoder; // actual encoder
core_file * m_file; // output file
UINT32 m_compressed_offset; // current offset with the compressed stream
FLAC__byte * m_compressed_start; // start of compressed data
UINT32 m_compressed_length; // length of the compressed stream
// parameters
UINT32 m_sample_rate; // sample rate
UINT8 m_channels; // number of channels
UINT32 m_block_size; // block size
// header stripping
bool m_strip_metadata; // strip the the metadata?
UINT32 m_ignore_bytes; // how many bytes to ignore when writing
bool m_found_audio; // have we hit the audio yet?
};
// ======================> flac_decoder
class flac_decoder
{
public:
// construction/destruction
flac_decoder();
flac_decoder(const void *buffer, UINT32 length, const void *buffer2 = NULL, UINT32 length2 = 0);
flac_decoder(core_file &file);
~flac_decoder();
// getters (valid after reset)
UINT32 sample_rate() const { return FLAC__stream_decoder_get_sample_rate(m_decoder); }
UINT8 channels() const { return FLAC__stream_decoder_get_channels(m_decoder); }
UINT32 block_size() const { return FLAC__stream_decoder_get_blocksize(m_decoder); }
FLAC__StreamDecoderState state() const { return FLAC__stream_decoder_get_state(m_decoder); }
const char *state_string() const { return FLAC__stream_decoder_get_resolved_state_string(m_decoder); }
// reset
bool reset();
bool reset(const void *buffer, UINT32 length, const void *buffer2 = NULL, UINT32 length2 = 0);
bool reset(UINT32 sample_rate, UINT8 num_channels, UINT32 block_size, const void *buffer, UINT32 length);
bool reset(core_file &file);
// decode to a buffer; num_samples must be a multiple of the block size
bool decode_interleaved(INT16 *samples, UINT32 num_samples, bool swap_endian = false);
bool decode(INT16 **samples, UINT32 num_samples, bool swap_endian = false);
// finish up
void finish();
private:
// internal helpers
static FLAC__StreamDecoderReadStatus read_callback_static(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data);
FLAC__StreamDecoderReadStatus read_callback(FLAC__byte buffer[], size_t *bytes);
static FLAC__StreamDecoderWriteStatus write_callback_static(const FLAC__StreamDecoder *decoder, const ::FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data);
FLAC__StreamDecoderWriteStatus write_callback(const ::FLAC__Frame *frame, const FLAC__int32 * const buffer[]);
static void error_callback_static(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data);
// output state
FLAC__StreamDecoder * m_decoder; // actual encoder
core_file * m_file; // output file
UINT32 m_compressed_offset; // current offset in compressed data
const FLAC__byte * m_compressed_start; // start of compressed data
UINT32 m_compressed_length; // length of compressed data
const FLAC__byte * m_compressed2_start; // start of compressed data
UINT32 m_compressed2_length; // length of compressed data
INT16 * m_uncompressed_start[8];// pointer to start of uncompressed data (up to 8 streams)
UINT32 m_uncompressed_offset; // current position in uncompressed data
UINT32 m_uncompressed_length; // length of uncompressed data
bool m_uncompressed_swap; // swap uncompressed sample data
UINT8 m_custom_header[0x2a]; // custom header
};
#endif // __FLAC_H__

View File

@ -46,13 +46,10 @@
TYPE DEFINITIONS
***************************************************************************/
struct _hard_disk_file
struct hard_disk_file
{
chd_file * chd; /* CHD file */
hard_disk_info info; /* hard disk info */
UINT32 hunksectors; /* sectors per hunk */
UINT32 cachehunk; /* which hunk is cached */
UINT8 * cache; /* cache of the current hunk */
};
@ -70,7 +67,7 @@ hard_disk_file *hard_disk_open(chd_file *chd)
{
int cylinders, heads, sectors, sectorbytes;
hard_disk_file *file;
char metadata[256];
astring metadata;
chd_error err;
/* punt if no CHD */
@ -78,7 +75,7 @@ hard_disk_file *hard_disk_open(chd_file *chd)
return NULL;
/* read the hard disk metadata */
err = chd_get_metadata(chd, HARD_DISK_METADATA_TAG, 0, metadata, sizeof(metadata), NULL, NULL, NULL);
err = chd->read_metadata(HARD_DISK_METADATA_TAG, 0, metadata);
if (err != CHDERR_NONE)
return NULL;
@ -97,17 +94,6 @@ hard_disk_file *hard_disk_open(chd_file *chd)
file->info.heads = heads;
file->info.sectors = sectors;
file->info.sectorbytes = sectorbytes;
file->hunksectors = chd_get_header(chd)->hunkbytes / file->info.sectorbytes;
file->cachehunk = -1;
/* allocate a cache */
file->cache = (UINT8 *)malloc(chd_get_header(chd)->hunkbytes);
if (file->cache == NULL)
{
free(file);
return NULL;
}
return file;
}
@ -118,9 +104,6 @@ hard_disk_file *hard_disk_open(chd_file *chd)
void hard_disk_close(hard_disk_file *file)
{
/* free the cache */
if (file->cache != NULL)
free(file->cache);
free(file);
}
@ -154,21 +137,8 @@ hard_disk_info *hard_disk_get_info(hard_disk_file *file)
UINT32 hard_disk_read(hard_disk_file *file, UINT32 lbasector, void *buffer)
{
UINT32 hunknum = lbasector / file->hunksectors;
UINT32 sectoroffs = lbasector % file->hunksectors;
/* if we haven't cached this hunk, read it now */
if (file->cachehunk != hunknum)
{
chd_error err = chd_read(file->chd, hunknum, file->cache);
if (err != CHDERR_NONE)
return 0;
file->cachehunk = hunknum;
}
/* copy out the requested sector */
memcpy(buffer, &file->cache[sectoroffs * file->info.sectorbytes], file->info.sectorbytes);
return 1;
chd_error err = file->chd->read_units(lbasector, buffer);
return (err == CHDERR_NONE);
}
@ -179,23 +149,6 @@ UINT32 hard_disk_read(hard_disk_file *file, UINT32 lbasector, void *buffer)
UINT32 hard_disk_write(hard_disk_file *file, UINT32 lbasector, const void *buffer)
{
UINT32 hunknum = lbasector / file->hunksectors;
UINT32 sectoroffs = lbasector % file->hunksectors;
chd_error err;
/* if we haven't cached this hunk, read it now */
if (file->cachehunk != hunknum)
{
err = chd_read(file->chd, hunknum, file->cache);
if (err != CHDERR_NONE)
return 0;
file->cachehunk = hunknum;
}
/* copy in the requested data */
memcpy(&file->cache[sectoroffs * file->info.sectorbytes], buffer, file->info.sectorbytes);
/* write it back out */
err = chd_write(file->chd, hunknum, file->cache);
return (err == CHDERR_NONE) ? 1 : 0;
chd_error err = file->chd->write_units(lbasector, buffer);
return (err == CHDERR_NONE);
}

View File

@ -50,10 +50,9 @@
TYPE DEFINITIONS
***************************************************************************/
typedef struct _hard_disk_file hard_disk_file;
struct hard_disk_file;
typedef struct _hard_disk_info hard_disk_info;
struct _hard_disk_info
struct hard_disk_info
{
UINT32 cylinders;
UINT32 heads;

282
src/lib/util/hashing.c Normal file
View File

@ -0,0 +1,282 @@
/***************************************************************************
hashing.c
Hashing helper classes.
****************************************************************************
Copyright Aaron Giles
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name 'MAME' nor the names of its contributors may be
used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
***************************************************************************/
#include "hashing.h"
//**************************************************************************
// CONSTANTS
//**************************************************************************
const crc16_t crc16_t::null = { 0 };
const crc32_t crc32_t::null = { 0 };
const md5_t md5_t::null = { { 0 } };
const sha1_t sha1_t::null = { { 0 } };
//**************************************************************************
// INLINE FUNCTIONS
//**************************************************************************
//-------------------------------------------------
// char_to_hex - return the hex value of a
// character
//-------------------------------------------------
inline int char_to_hex(char c)
{
if (c >= '0' && c <= '9')
return c - '0';
if (c >= 'a' && c <= 'f')
return 10 + c - 'a';
if (c >= 'A' && c <= 'F')
return 10 + c - 'a';
return -1;
}
//**************************************************************************
// SHA-1 HELPERS
//**************************************************************************
//-------------------------------------------------
// from_string - convert from a string
//-------------------------------------------------
bool sha1_t::from_string(const char *string)
{
// must be at least long enough to hold everything
if (strlen(string) < 2 * sizeof(m_raw))
return false;
// iterate through our raw buffer
for (int bytenum = 0; bytenum < sizeof(m_raw); bytenum++)
{
int upper = char_to_hex(*string++);
int lower = char_to_hex(*string++);
if (upper == -1 || lower == -1)
return false;
m_raw[bytenum] = (upper << 4) | lower;
}
return true;
}
//-------------------------------------------------
// as_string - convert to a string
//-------------------------------------------------
const char *sha1_t::as_string(astring &buffer)
{
buffer.reset();
for (int i = 0; i < ARRAY_LENGTH(m_raw); i++)
buffer.catformat("%02x", m_raw[i]);
return buffer;
}
//**************************************************************************
// MD-5 HELPERS
//**************************************************************************
//-------------------------------------------------
// from_string - convert from a string
//-------------------------------------------------
bool md5_t::from_string(const char *string)
{
// must be at least long enough to hold everything
if (strlen(string) < 2 * sizeof(m_raw))
return false;
// iterate through our raw buffer
for (int bytenum = 0; bytenum < sizeof(m_raw); bytenum++)
{
int upper = char_to_hex(*string++);
int lower = char_to_hex(*string++);
if (upper == -1 || lower == -1)
return false;
m_raw[bytenum] = (upper << 4) | lower;
}
return true;
}
//-------------------------------------------------
// as_string - convert to a string
//-------------------------------------------------
const char *md5_t::as_string(astring &buffer)
{
buffer.reset();
for (int i = 0; i < ARRAY_LENGTH(m_raw); i++)
buffer.catformat("%02x", m_raw[i]);
return buffer;
}
//**************************************************************************
// CRC-32 HELPERS
//**************************************************************************
//-------------------------------------------------
// from_string - convert from a string
//-------------------------------------------------
bool crc32_t::from_string(const char *string)
{
// must be at least long enough to hold everything
if (strlen(string) < 2 * sizeof(m_raw))
return false;
// iterate through our raw buffer
m_raw = 0;
for (int bytenum = 0; bytenum < sizeof(m_raw) * 2; bytenum++)
{
int nibble = char_to_hex(*string++);
if (nibble == -1)
return false;
m_raw = (m_raw << 4) | nibble;
}
return true;
}
//-------------------------------------------------
// as_string - convert to a string
//-------------------------------------------------
const char *crc32_t::as_string(astring &buffer)
{
return buffer.format("%08x", m_raw);
}
//**************************************************************************
// CRC-16 HELPERS
//**************************************************************************
//-------------------------------------------------
// from_string - convert from a string
//-------------------------------------------------
bool crc16_t::from_string(const char *string)
{
// must be at least long enough to hold everything
if (strlen(string) < 2 * sizeof(m_raw))
return false;
// iterate through our raw buffer
m_raw = 0;
for (int bytenum = 0; bytenum < sizeof(m_raw) * 2; bytenum++)
{
int nibble = char_to_hex(*string++);
if (nibble == -1)
return false;
m_raw = (m_raw << 4) | nibble;
}
return true;
}
//-------------------------------------------------
// as_string - convert to a string
//-------------------------------------------------
const char *crc16_t::as_string(astring &buffer)
{
return buffer.format("%04x", m_raw);
}
//-------------------------------------------------
// append - hash a block of data, appending to
// the currently-accumulated value
//-------------------------------------------------
void crc16_creator::append(const void *data, UINT32 length)
{
static const UINT16 s_table[256] =
{
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
};
const UINT8 *src = reinterpret_cast<const UINT8 *>(data);
// fetch the current value into a local and rip through the source data
UINT16 crc = m_accum.m_raw;
while (length-- != 0)
crc = (crc << 8) ^ s_table[(crc >> 8) ^ *src++];
m_accum.m_raw = crc;
}

245
src/lib/util/hashing.h Normal file
View File

@ -0,0 +1,245 @@
/***************************************************************************
hashing.h
Hashing helper classes.
****************************************************************************
Copyright Aaron Giles
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name 'MAME' nor the names of its contributors may be
used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
***************************************************************************/
#pragma once
#ifndef __HASHING_H__
#define __HASHING_H__
#include "osdcore.h"
#include "astring.h"
#include "zlib.h"
#include "md5.h"
#include "sha1.h"
//**************************************************************************
// TYPE DEFINITIONS
//**************************************************************************
// ======================> SHA-1
// final digest
struct sha1_t
{
bool operator==(const sha1_t &rhs) const { return memcmp(m_raw, rhs.m_raw, sizeof(m_raw)) == 0; }
bool operator!=(const sha1_t &rhs) const { return memcmp(m_raw, rhs.m_raw, sizeof(m_raw)) != 0; }
operator UINT8 *() { return m_raw; }
const char *as_string(astring &buffer);
bool from_string(const char *string);
UINT8 m_raw[20];
static const sha1_t null;
};
// creation helper
class sha1_creator
{
public:
// construction/destruction
sha1_creator() { reset(); }
// reset
void reset() { sha1_init(&m_context); }
// append data
void append(const void *data, UINT32 length) { sha1_update(&m_context, length, reinterpret_cast<const UINT8 *>(data)); }
// finalize and compute the final digest
sha1_t finish()
{
sha1_t result;
sha1_final(&m_context);
sha1_digest(&m_context, sizeof(result.m_raw), result.m_raw);
return result;
}
// static wrapper to just get the digest from a block
static sha1_t simple(const void *data, UINT32 length)
{
sha1_creator creator;
creator.append(data, length);
return creator.finish();
}
protected:
// internal state
struct sha1_ctx m_context; // internal context
};
// ======================> MD5
// final digest
struct md5_t
{
bool operator==(const md5_t &rhs) const { return memcmp(m_raw, rhs.m_raw, sizeof(m_raw)) == 0; }
bool operator!=(const md5_t &rhs) const { return memcmp(m_raw, rhs.m_raw, sizeof(m_raw)) != 0; }
operator UINT8 *() { return m_raw; }
const char *as_string(astring &buffer);
bool from_string(const char *string);
UINT8 m_raw[16];
static const md5_t null;
};
// creation helper
class md5_creator
{
public:
// construction/destruction
md5_creator() { reset(); }
// reset
void reset() { MD5Init(&m_context); }
// append data
void append(const void *data, UINT32 length) { MD5Update(&m_context, reinterpret_cast<const unsigned char *>(data), length); }
// finalize and compute the final digest
md5_t finish()
{
md5_t result;
MD5Final(result.m_raw, &m_context);
return result;
}
// static wrapper to just get the digest from a block
static md5_t simple(const void *data, UINT32 length)
{
md5_creator creator;
creator.append(data, length);
return creator.finish();
}
protected:
// internal state
struct MD5Context m_context; // internal context
};
// ======================> CRC-32
// final digest
struct crc32_t
{
bool operator==(const crc32_t &rhs) const { return m_raw == rhs.m_raw; }
operator UINT32() const { return m_raw; }
const char *as_string(astring &buffer);
bool from_string(const char *string);
UINT32 m_raw;
static const crc32_t null;
};
// creation helper
class crc32_creator
{
public:
// construction/destruction
crc32_creator() { reset(); }
// reset
void reset() { m_accum.m_raw = 0; }
// append data
void append(const void *data, UINT32 length) { m_accum.m_raw = crc32(m_accum, reinterpret_cast<const Bytef *>(data), length); }
// finalize and compute the final digest
crc32_t finish() { return m_accum; }
// static wrapper to just get the digest from a block
static crc32_t simple(const void *data, UINT32 length)
{
crc32_creator creator;
creator.append(data, length);
return creator.finish();
}
protected:
// internal state
crc32_t m_accum; // internal accumulator
};
// ======================> CRC-16
// final digest
struct crc16_t
{
bool operator==(const crc16_t &rhs) const { return m_raw == rhs.m_raw; }
operator UINT16() const { return m_raw; }
const char *as_string(astring &buffer);
bool from_string(const char *string);
UINT16 m_raw;
static const crc16_t null;
};
// creation helper
class crc16_creator
{
public:
// construction/destruction
crc16_creator() { reset(); }
// reset
void reset() { m_accum.m_raw = 0xffff; }
// append data
void append(const void *data, UINT32 length);
// finalize and compute the final digest
crc16_t finish() { return m_accum; }
// static wrapper to just get the digest from a block
static crc16_t simple(const void *data, UINT32 length)
{
crc16_creator creator;
creator.append(data, length);
return creator.finish();
}
protected:
// internal state
crc16_t m_accum; // internal accumulator
};
#endif // __HASHING_H__

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,7 @@
huffman.h
Huffman compression routines.
Static Huffman compression and decompression helpers.
****************************************************************************
@ -37,19 +37,22 @@
***************************************************************************/
#pragma once
#ifndef __HUFFMAN_H__
#define __HUFFMAN_H__
#include "osdcore.h"
#include "bitstream.h"
/***************************************************************************
CONSTANTS
***************************************************************************/
//**************************************************************************
// CONSTANTS
//**************************************************************************
enum _huffman_error
enum huffman_error
{
HUFFERR_NONE = 0,
HUFFERR_OUT_OF_MEMORY,
HUFFERR_TOO_MANY_BITS,
HUFFERR_INVALID_DATA,
HUFFERR_INPUT_BUFFER_TOO_SMALL,
@ -57,45 +60,193 @@ enum _huffman_error
HUFFERR_INTERNAL_INCONSISTENCY,
HUFFERR_TOO_MANY_CONTEXTS
};
typedef enum _huffman_error huffman_error;
/***************************************************************************
TYPE DEFINITIONS
***************************************************************************/
//**************************************************************************
// TYPE DEFINITIONS
//**************************************************************************
typedef UINT16 huffman_lookup_value;
// ======================> huffman_context_base
typedef struct _huffman_context huffman_context;
// base class for encoding and decoding
class huffman_context_base
{
protected:
typedef UINT16 lookup_value;
// a node in the huffman tree
struct node_t
{
node_t * m_parent; // pointer to parent node
UINT32 m_count; // number of hits on this node
UINT32 m_weight; // assigned weight of this node
UINT32 m_bits; // bits used to encode the node
UINT8 m_numbits; // number of bits needed for this node
};
// construction/destruction
huffman_context_base(int numcodes, int maxbits, lookup_value *lookup, UINT32 *histo, node_t *nodes);
// tree creation
huffman_error compute_tree_from_histo();
// static tree import; huffman is notably more efficient
huffman_error import_tree_rle(bitstream_in &bitbuf);
huffman_error import_tree_huffman(bitstream_in &bitbuf);
// static tree export
huffman_error export_tree_rle(bitstream_out &bitbuf);
huffman_error export_tree_huffman(bitstream_out &bitbuf);
// internal helpers
void write_rle_tree_bits(bitstream_out &bitbuf, int value, int repcount, int numbits);
static int CLIB_DECL tree_node_compare(const void *item1, const void *item2);
int build_tree(UINT32 totaldata, UINT32 totalweight);
huffman_error assign_canonical_codes();
void build_lookup_table();
protected:
// internal state
UINT32 m_numcodes; // number of total codes being processed
UINT8 m_maxbits; // maximum bits per code
UINT8 m_prevdata; // value of the previous data (for delta-RLE encoding)
int m_rleremaining; // number of RLE bytes remaining (for delta-RLE encoding)
lookup_value * m_lookup; // pointer to the lookup table
UINT32 * m_datahisto; // histogram of data values
node_t * m_huffnode; // array of nodes
};
// ======================> huffman_encoder
// template class for encoding
template<int _NumCodes = 256, int _MaxBits = 16>
class huffman_encoder : public huffman_context_base
{
public:
// pass through to the underlying constructor
huffman_encoder()
: huffman_context_base(_NumCodes, _MaxBits, NULL, m_datahisto_array, m_huffnode_array) { histo_reset(); }
// single item operations
void histo_reset() { memset(m_datahisto_array, 0, sizeof(m_datahisto_array)); }
void histo_one(UINT32 data);
void encode_one(bitstream_out &bitbuf, UINT32 data);
// expose tree computation and export
using huffman_context_base::compute_tree_from_histo;
using huffman_context_base::export_tree_rle;
using huffman_context_base::export_tree_huffman;
private:
// array versions of the info we need
UINT32 m_datahisto_array[_NumCodes];
node_t m_huffnode_array[_NumCodes * 2];
};
// ======================> huffman_decoder
// template class for decoding
template<int _NumCodes = 256, int _MaxBits = 16>
class huffman_decoder : public huffman_context_base
{
public:
// pass through to the underlying constructor
huffman_decoder()
: huffman_context_base(_NumCodes, _MaxBits, m_lookup_array, NULL, m_huffnode_array) { }
// single item operations
UINT32 decode_one(bitstream_in &bitbuf);
// expose tree import
using huffman_context_base::import_tree_rle;
using huffman_context_base::import_tree_huffman;
private:
// array versions of the info we need
node_t m_huffnode_array[_NumCodes];
lookup_value m_lookup_array[1 << _MaxBits];
};
// ======================> huffman_8bit_encoder
// generic 8-bit encoder/decoder
class huffman_8bit_encoder : public huffman_encoder<>
{
public:
// construction/destruction
huffman_8bit_encoder();
// operations
huffman_error encode(const UINT8 *source, UINT32 slength, UINT8 *dest, UINT32 destlength, UINT32 &complength);
};
// ======================> huffman_8bit_decoder
// generic 8-bit encoder/decoder
class huffman_8bit_decoder : public huffman_decoder<>
{
public:
// construction/destruction
huffman_8bit_decoder();
// operations
huffman_error decode(const UINT8 *source, UINT32 slength, UINT8 *dest, UINT32 destlength);
};
/***************************************************************************
FUNCTION PROTOTYPES
***************************************************************************/
//**************************************************************************
// INLINE FUNCTIONS
//**************************************************************************
huffman_error huffman_create_context(huffman_context **context, int maxbits);
void huffman_free_context(huffman_context *context);
//-------------------------------------------------
// histo_one - update the histogram
//-------------------------------------------------
huffman_error huffman_import_tree(huffman_context *context, const UINT8 *source, UINT32 slength, UINT32 *actlength);
huffman_error huffman_export_tree(huffman_context *context, UINT8 *dest, UINT32 dlength, UINT32 *actlength);
huffman_error huffman_deltarle_import_tree(huffman_context *context, const UINT8 *source, UINT32 slength, UINT32 *actlength);
huffman_error huffman_deltarle_export_tree(huffman_context *context, UINT8 *dest, UINT32 dlength, UINT32 *actlength);
template<int _NumCodes, int _MaxBits>
inline void huffman_encoder<_NumCodes, _MaxBits>::histo_one(UINT32 data)
{
m_datahisto[data]++;
}
huffman_error huffman_compute_tree(huffman_context *context, const UINT8 *source, UINT32 swidth, UINT32 sheight, UINT32 sstride, UINT32 sxor);
huffman_error huffman_compute_tree_interleaved(int numcontexts, huffman_context **contexts, const UINT8 *source, UINT32 swidth, UINT32 sheight, UINT32 sstride, UINT32 sxor);
huffman_error huffman_deltarle_compute_tree(huffman_context *context, const UINT8 *source, UINT32 swidth, UINT32 sheight, UINT32 sstride, UINT32 sxor);
huffman_error huffman_deltarle_compute_tree_interleaved(int numcontexts, huffman_context **contexts, const UINT8 *source, UINT32 swidth, UINT32 sheight, UINT32 sstride, UINT32 sxor);
huffman_error huffman_encode_data(huffman_context *context, const UINT8 *source, UINT32 swidth, UINT32 sheight, UINT32 sstride, UINT32 sxor, UINT8 *dest, UINT32 dlength, UINT32 *actlength);
huffman_error huffman_encode_data_interleaved(int numcontexts, huffman_context **contexts, const UINT8 *source, UINT32 swidth, UINT32 sheight, UINT32 sstride, UINT32 sxor, UINT8 *dest, UINT32 dlength, UINT32 *actlength);
huffman_error huffman_deltarle_encode_data(huffman_context *context, const UINT8 *source, UINT32 swidth, UINT32 sheight, UINT32 sstride, UINT32 sxor, UINT8 *dest, UINT32 dlength, UINT32 *actlength);
huffman_error huffman_deltarle_encode_data_interleaved(int numcontexts, huffman_context **contexts, const UINT8 *source, UINT32 swidth, UINT32 sheight, UINT32 sstride, UINT32 sxor, UINT8 *dest, UINT32 dlength, UINT32 *actlength);
//-------------------------------------------------
// encode_one - encode a single code to the
// huffman stream
//-------------------------------------------------
template<int _NumCodes, int _MaxBits>
inline void huffman_encoder<_NumCodes, _MaxBits>::encode_one(bitstream_out &bitbuf, UINT32 data)
{
// write the data
node_t &node = m_huffnode[data];
bitbuf.write(node.m_bits, node.m_numbits);
}
//-------------------------------------------------
// decode_one - decode a single code from the
// huffman stream
//-------------------------------------------------
template<int _NumCodes, int _MaxBits>
inline UINT32 huffman_decoder<_NumCodes, _MaxBits>::decode_one(bitstream_in &bitbuf)
{
// peek ahead to get maxbits worth of data
UINT32 bits = bitbuf.peek(m_maxbits);
// look it up, then remove the actual number of bits for this code
lookup_value lookup = m_lookup[bits];
bitbuf.remove(lookup & 0x1f);
// return the value
return lookup >> 5;
}
huffman_error huffman_decode_data(huffman_context *context, const UINT8 *source, UINT32 slength, UINT8 *dest, UINT32 dwidth, UINT32 dheight, UINT32 dstride, UINT32 dxor, UINT32 *actlength);
huffman_error huffman_decode_data_interleaved(int numcontexts, huffman_context **contexts, const UINT8 *source, UINT32 slength, UINT8 *dest, UINT32 dwidth, UINT32 dheight, UINT32 dstride, UINT32 dxor, UINT32 *actlength);
huffman_error huffman_deltarle_decode_data(huffman_context *context, const UINT8 *source, UINT32 slength, UINT8 *dest, UINT32 dwidth, UINT32 dheight, UINT32 dstride, UINT32 dxor, UINT32 *actlength);
huffman_error huffman_deltarle_decode_data_interleaved(int numcontexts, huffman_context **contexts, const UINT8 *source, UINT32 slength, UINT8 *dest, UINT32 dwidth, UINT32 dheight, UINT32 dstride, UINT32 dxor, UINT32 *actlength);
#endif

View File

@ -158,7 +158,7 @@ public:
for (entry_t *entry = m_table[fullhash % ARRAY_LENGTH(m_table)]; entry != NULL; entry = entry->next())
if (entry->fullhash() == fullhash && entry->tag() == tag)
return entry->object();
return (_ElementType)NULL;
return _ElementType(NULL);
}
// find by tag without checking anything but the hash

View File

@ -451,7 +451,7 @@ static WRITE32_HANDLER(rf5c296_mem_w)
taitogn_state *state = space->machine().driver_data<taitogn_state>();
if(offset >= 0x140 && offset <= 0x144) {
UINT8 key[5];
dynamic_buffer key;
int pos = (offset - 0x140)*2;
UINT8 v, k;
if(ACCESSING_BITS_16_23) {
@ -459,8 +459,8 @@ static WRITE32_HANDLER(rf5c296_mem_w)
pos++;
} else
v = data;
chd_get_metadata(get_disk_handle(space->machine(), ":card"), HARD_DISK_KEY_METADATA_TAG, 0, key, 5, 0, 0, 0);
k = pos < 5 ? key[pos] : 0;
get_disk_handle(space->machine(), ":card")->read_metadata(HARD_DISK_KEY_METADATA_TAG, 0, key);
k = pos < key.count() ? key[pos] : 0;
if(v == k)
state->m_locked &= ~(1 << pos);
else
@ -889,9 +889,10 @@ static DRIVER_INIT( coh3002t )
psx_sio_install_handler(machine, 0, sio_pad_handler);
state->m_dip_timer = machine.scheduler().timer_alloc( FUNC(dip_timer_fired), NULL );
UINT32 metalength;
memset(state->m_cis, 0xff, 512);
if (get_disk_handle(machine, ":card") != NULL)
chd_get_metadata(get_disk_handle(machine, ":card"), PCMCIA_CIS_METADATA_TAG, 0, state->m_cis, 512, 0, 0, 0);
get_disk_handle(machine, ":card")->read_metadata(PCMCIA_CIS_METADATA_TAG, 0, state->m_cis, 512, metalength);
}
static DRIVER_INIT( coh3002t_mp )

View File

@ -104,6 +104,8 @@ osd_ticks_t osd_ticks(void)
osd_ticks_t osd_ticks_per_second(void)
{
if (ticks_per_second == 0)
osd_ticks();
return ticks_per_second;
}

View File

@ -321,9 +321,14 @@ osd_work_queue *osd_work_queue_alloc(int flags)
if (numprocs == 1)
queue->threads = (flags & WORK_QUEUE_FLAG_IO) ? 1 : 0;
// on an n-CPU system, create (n-1) threads for multi queues, and 1 thread for everything else
// on an n-CPU system, create n threads for multi queues, and 1 thread for everything else
else
queue->threads = (flags & WORK_QUEUE_FLAG_MULTI) ? (numprocs - 1) : 1;
queue->threads = (flags & WORK_QUEUE_FLAG_MULTI) ? numprocs : 1;
// multi-queues with high frequency items should top out at 4 for now
// since we have scaling problems above that
if ((flags & WORK_QUEUE_FLAG_HIGH_FREQ) && queue->threads > 1)
queue->threads = MIN(queue->threads - 1, 4);
// clamp to the maximum
queue->threads = MIN(queue->threads, WORK_MAX_THREADS);
@ -706,13 +711,12 @@ static int effective_num_processors(void)
int numprocs = 0;
// if the OSDPROCESSORS environment variable is set, use that value if valid
// note that we permit more than the real number of processors for testing
procsoverride = _tgetenv(_T("OSDPROCESSORS"));
if (procsoverride != NULL && _stscanf(procsoverride, _T("%d"), &numprocs) == 1 && numprocs > 0)
// Be well behaved ...
return MIN(info.dwNumberOfProcessors * 4, numprocs);
// max out at 4 for now since scaling above that seems to do poorly
return MIN(info.dwNumberOfProcessors, 4);
return info.dwNumberOfProcessors;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -84,7 +84,8 @@ romcmp$(EXE): $(ROMCMPOBJS) $(LIBUTIL) $(ZLIB) $(EXPAT) $(LIBOCORE)
CHDMANOBJS = \
$(TOOLSOBJ)/chdman.o \
chdman$(EXE): $(VERSIONOBJ) $(CHDMANOBJS) $(LIBUTIL) $(ZLIB) $(EXPAT) $(FLAC_LIB) $(LIBOCORE)
chdman$(EXE): $(VERSIONOBJ) $(CHDMANOBJS) $(LIBUTIL) $(ZLIB) $(EXPAT) $(FLAC_LIB) $(7Z_LIB) $(LIBOCORE)
$(CC) $(CDEFS) $(CFLAGS) -c $(SRC)/version.c -o $(VERSIONOBJ)
@echo Linking $@...
$(LD) $(LDFLAGS) $^ $(LIBS) $(FLAC_LIB) -o $@