mirror of
https://github.com/holub/mame
synced 2025-07-05 18:08:04 +03:00
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:
parent
e6dad37593
commit
f0823886a6
12
.gitattributes
vendored
12
.gitattributes
vendored
@ -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
|
||||
|
4
makefile
4
makefile
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -59,7 +59,6 @@
|
||||
#include "profiler.h"
|
||||
|
||||
// commonly-referenecd utilities imported from lib/util
|
||||
#include "chd.h"
|
||||
#include "palette.h"
|
||||
#include "unicode.h"
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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 ¤t_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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 $@
|
||||
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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"
|
||||
|
||||
|
@ -52,7 +52,7 @@
|
||||
// TYPE DEFINITIONS
|
||||
//**************************************************************************
|
||||
|
||||
// derived class for C++
|
||||
// basic allocated string class
|
||||
class astring
|
||||
{
|
||||
public:
|
||||
|
@ -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;
|
||||
}
|
@ -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
977
src/lib/util/avhuff.c
Normal 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
231
src/lib/util/avhuff.h
Normal 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
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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
265
src/lib/util/bitstream.h
Normal 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
|
@ -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, §oroffs, &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, §oroffs, &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;
|
||||
}
|
||||
|
@ -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];
|
||||
|
5668
src/lib/util/chd.c
5668
src/lib/util/chd.c
File diff suppressed because it is too large
Load Diff
@ -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__
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
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
212
src/lib/util/chdcodec.h
Normal 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__
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
|
@ -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
109
src/lib/util/coretmpl.h
Normal 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
598
src/lib/util/flac.c
Normal 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
167
src/lib/util/flac.h
Normal 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__
|
@ -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);
|
||||
}
|
||||
|
@ -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
282
src/lib/util/hashing.c
Normal 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
245
src/lib/util/hashing.h
Normal 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
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 )
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
5562
src/tools/chdman.c
5562
src/tools/chdman.c
File diff suppressed because it is too large
Load Diff
@ -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 $@
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user