disc - add a project including mednafen's disc-loading code, for a/b testing bizhawk's disc loading on large disc sets, and add an example a/b test using it.

This commit is contained in:
zeromus 2014-12-25 19:56:04 +00:00
parent 313c0a1c27
commit e80de55210
70 changed files with 16008 additions and 0 deletions

View File

@ -100,6 +100,7 @@
<Compile Include="MainDiscoForm.Designer.cs">
<DependentUpon>MainDiscoForm.cs</DependentUpon>
</Compile>
<Compile Include="MednadiscTester.cs" />
<Compile Include="ProgressDialog.cs">
<SubType>Form</SubType>
</Compile>

View File

@ -201,6 +201,8 @@ namespace BizHawk.Client.DiscoHawk
else
{
//test stuff...
MednadiscTester tester = new MednadiscTester();
tester.TestDirectory(@"c:\isos\psx");
}
}
}

View File

@ -0,0 +1,121 @@
using System;
using System.IO;
using System.Runtime.InteropServices;
using BizHawk.Emulation.DiscSystem;
class MednadiscTester
{
[DllImport("mednadisc.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr mednadisc_LoadCD(string path);
[DllImport("mednadisc.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int mednadisc_ReadSector(IntPtr disc, int lba, byte[] buf2448);
[DllImport("mednadisc.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void mednadisc_CloseCD(IntPtr disc);
public class QuickSubcodeReader
{
public QuickSubcodeReader(byte[] buffer)
{
this.buffer = buffer;
}
public void ReadLBA_SubchannelQ(int offset, ref SubchannelQ sq)
{
sq.q_status = buffer[offset + 0];
sq.q_tno = buffer[offset + 1];
sq.q_index = buffer[offset + 2];
sq.min.BCDValue = buffer[offset + 3];
sq.sec.BCDValue = buffer[offset + 4];
sq.frame.BCDValue = buffer[offset + 5];
//nothing in byte[6]
sq.ap_min.BCDValue = buffer[offset + 7];
sq.ap_sec.BCDValue = buffer[offset + 8];
sq.ap_frame.BCDValue = buffer[offset + 9];
//CRC is stored inverted and big endian.. so... do the opposite
byte hibyte = (byte)(~buffer[offset + 10]);
byte lobyte = (byte)(~buffer[offset + 11]);
sq.q_crc = (ushort)((hibyte << 8) | lobyte);
}
byte[] buffer;
}
public void TestDirectory(string dpTarget)
{
foreach (var fi in new DirectoryInfo(dpTarget).GetFiles())
{
if (fi.Extension.ToLower() == ".cue") { }
else if (fi.Extension.ToLower() == ".ccd") { }
else continue;
NewTest(fi.FullName);
}
}
static bool NewTest(string path)
{
bool ret = false;
Disc disc;
if (Path.GetExtension(path).ToLower() == ".cue") disc = Disc.FromCuePath(path, new CueBinPrefs());
else disc = Disc.FromCCDPath(path);
IntPtr mednadisc = mednadisc_LoadCD(path);
//TODO - test leadout a bit, or determine length some superior way
//TODO - check length against mednadisc
int nSectors = (int)(disc.Structure.BinarySize / 2352) - 150;
var subbuf = new byte[96];
var discbuf = new byte[2352 + 96];
var monkeybuf = new byte[2352 + 96];
var disc_qbuf = new byte[96];
var monkey_qbuf = new byte[96];
for (int i = 0; i < nSectors; i++)
{
mednadisc_ReadSector(mednadisc, i, monkeybuf);
disc.ReadLBA_2352(i, discbuf, 0);
disc.ReadLBA_SectorEntry(i).SubcodeSector.ReadSubcodeDeinterleaved(subbuf, 0);
SubcodeUtils.Interleave(subbuf, 0, discbuf, 2352);
//remove P
for (int q = 2352; q < 2352 + 96; q++)
{
discbuf[q] &= 0x7F;
monkeybuf[q] &= 0x7F;
}
for (int q = 0; q < 2352 + 96; q++)
{
if (discbuf[q] != monkeybuf[q])
{
Console.WriteLine("MISMATCH: " + Path.GetFileName(path));
//decode Q subchannels for manual investigation
SubcodeUtils.Deinterleave(discbuf, 2352, disc_qbuf, 0);
var asr = new QuickSubcodeReader(disc_qbuf);
SubchannelQ disc_q = new SubchannelQ();
asr.ReadLBA_SubchannelQ(12, ref disc_q);
SubcodeUtils.Deinterleave(monkeybuf, 2352, monkey_qbuf, 0);
asr = new QuickSubcodeReader(monkey_qbuf);
SubchannelQ monkey_q = new SubchannelQ();
asr.ReadLBA_SubchannelQ(12, ref monkey_q);
goto END;
}
}
}
ret = true;
END:
disc.Dispose();
mednadisc_CloseCD(mednadisc);
return ret;
}
}

View File

@ -0,0 +1,349 @@
/* Mednafen - Multi-system Emulator
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "emuware/emuware.h"
#include "FileStream.h"
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
//#include <unistd.h>
#include <string.h>
#include <stdlib.h>
//work around gettext
#define _(X) X
#ifdef _MSC_VER
#include <sys/stat.h>
#include <io.h>
// These are not defined in MSVC version of stat.h
#define S_IWUSR S_IWRITE
#define S_IRUSR S_IREAD
#endif
#ifdef HAVE_MMAP
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#endif
// Some really bad preprocessor abuse follows to handle platforms that don't have fseeko and ftello...and of course
// for largefile support on Windows:
#ifndef HAVE_FSEEKO
#define fseeko fseek
#endif
#ifndef HAVE_FTELLO
#define ftello ftell
#endif
#define STRUCT_STAT struct stat
#if SIZEOF_OFF_T == 4
#ifdef HAVE_FOPEN64
#define fopen fopen64
#endif
#ifdef HAVE_FTELLO64
#undef ftello
#define ftello ftello64
#endif
#ifdef HAVE_FSEEKO64
#undef fseeko
#define fseeko fseeko64
#endif
#ifdef HAVE_FSTAT64
#define fstat fstat64
#define stat stat64
#undef STRUCT_STAT
#define STRUCT_STAT struct stat64
#endif
#ifdef HAVE_FTRUNCATE64
#define ftruncate ftruncate64
#endif
#endif
FileStream::FileStream(const std::string& path, const int mode) : OpenedMode(mode), mapping(NULL), mapping_size(0)
{
path_save = path;
if(mode == MODE_READ)
fp = fopen(path.c_str(), "rb");
else if(mode == MODE_WRITE)
fp = fopen(path.c_str(), "wb");
else if(mode == MODE_WRITE_SAFE || mode == MODE_WRITE_INPLACE) // SO ANNOYING
{
int open_flags = O_WRONLY | O_CREAT;
if(mode == MODE_WRITE_SAFE)
open_flags |= O_EXCL;
#ifdef O_BINARY
open_flags |= O_BINARY;
#elif defined(_O_BINARY)
open_flags |= _O_BINARY;
#endif
#if defined(S_IRGRP) && defined(S_IROTH)
int tmpfd = open(path.c_str(), open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
#else
int tmpfd = open(path.c_str(), open_flags, S_IRUSR | S_IWUSR);
#endif
if(tmpfd == -1)
{
ErrnoHolder ene(errno);
throw(MDFN_Error(ene.Errno(), _("Error opening file \"%s\": %s"), path_save.c_str(), ene.StrError()));
}
fp = fdopen(tmpfd, "wb");
}
else
abort();
if(!fp)
{
ErrnoHolder ene(errno);
throw(MDFN_Error(ene.Errno(), _("Error opening file \"%s\": %s"), path_save.c_str(), ene.StrError()));
}
}
FileStream::~FileStream()
{
try
{
close();
}
catch(std::exception &e)
{
printf(e.what());
}
}
uint64 FileStream::attributes(void)
{
uint64 ret = ATTRIBUTE_SEEKABLE;
switch(OpenedMode)
{
case MODE_READ:
ret |= ATTRIBUTE_READABLE;
break;
case MODE_WRITE_INPLACE:
case MODE_WRITE_SAFE:
case MODE_WRITE:
ret |= ATTRIBUTE_WRITEABLE;
break;
}
return ret;
}
uint8 *FileStream::map(void) noexcept
{
if(!mapping)
{
#ifdef HAVE_MMAP
uint64 length = size();
int prot = 0;
int flags = 0;
void* tptr;
if(OpenedMode == MODE_READ)
{
prot |= PROT_READ; // | PROT_EXEC;
flags |= MAP_PRIVATE;
}
else
{
prot |= PROT_WRITE;
prot |= MAP_SHARED;
}
if(length > SIZE_MAX)
return(NULL);
tptr = mmap(NULL, length, prot, flags, fileno(fp), 0);
if(tptr != (void*)-1)
{
mapping = tptr;
mapping_size = length;
#ifdef HAVE_MADVISE
// Should probably make this controllable via flag or somesuch.
madvise(mapping, mapping_size, MADV_SEQUENTIAL | MADV_WILLNEED);
#endif
}
#endif
}
return((uint8*)mapping);
}
uint64 FileStream::map_size(void) noexcept
{
return mapping_size;
}
void FileStream::unmap(void) noexcept
{
if(mapping)
{
#ifdef HAVE_MMAP
munmap(mapping, mapping_size);
#endif
mapping = NULL;
mapping_size = 0;
}
}
uint64 FileStream::read(void *data, uint64 count, bool error_on_eos)
{
uint64 read_count;
clearerr(fp);
read_count = fread(data, 1, count, fp);
if(read_count != count)
{
ErrnoHolder ene(errno);
if(ferror(fp))
throw(MDFN_Error(ene.Errno(), _("Error reading from opened file \"%s\": %s"), path_save.c_str(), ene.StrError()));
if(error_on_eos)
throw(MDFN_Error(0, _("Error reading from opened file \"%s\": %s"), path_save.c_str(), _("Unexpected EOF")));
}
return(read_count);
}
void FileStream::write(const void *data, uint64 count)
{
if(fwrite(data, 1, count, fp) != count)
{
ErrnoHolder ene(errno);
throw(MDFN_Error(ene.Errno(), _("Error writing to opened file \"%s\": %s"), path_save.c_str(), ene.StrError()));
}
}
void FileStream::truncate(uint64 length)
{
//not needed by mednadisc
//if(fflush(fp) == EOF || ftruncate(fileno(fp), length) != 0)
{
ErrnoHolder ene(errno);
throw(MDFN_Error(ene.Errno(), _("Error truncating opened file \"%s\": %s"), path_save.c_str(), ene.StrError()));
}
}
void FileStream::seek(int64 offset, int whence)
{
if(fseeko(fp, offset, whence) == -1)
{
ErrnoHolder ene(errno);
throw(MDFN_Error(ene.Errno(), _("Error seeking in opened file \"%s\": %s"), path_save.c_str(), ene.StrError()));
}
}
void FileStream::flush(void)
{
if(fflush(fp) == EOF)
{
ErrnoHolder ene(errno);
throw(MDFN_Error(ene.Errno(), _("Error flushing to opened file \"%s\": %s"), path_save.c_str(), ene.StrError()));
}
}
uint64 FileStream::tell(void)
{
auto offset = ftello(fp);
if(offset == -1)
{
ErrnoHolder ene(errno);
throw(MDFN_Error(ene.Errno(), _("Error getting position in opened file \"%s\": %s"), path_save.c_str(), ene.StrError()));
}
return (std::make_unsigned<decltype(offset)>::type)offset;
}
uint64 FileStream::size(void)
{
STRUCT_STAT buf;
if((OpenedMode != MODE_READ && fflush(fp) == EOF) || fstat(fileno(fp), &buf) == -1)
{
ErrnoHolder ene(errno);
throw(MDFN_Error(ene.Errno(), _("Error getting the size of opened file \"%s\": %s"), path_save.c_str(), ene.StrError()));
}
return (std::make_unsigned<decltype(buf.st_size)>::type)buf.st_size;
}
void FileStream::close(void)
{
if(fp)
{
FILE *tmp = fp;
unmap();
fp = NULL;
if(fclose(tmp) == EOF)
{
ErrnoHolder ene(errno);
throw(MDFN_Error(ene.Errno(), _("Error closing opened file \"%s\": %s"), path_save.c_str(), ene.StrError()));
}
}
}
int FileStream::get_line(std::string &str)
{
int c;
str.clear();
while((c = get_char()) >= 0)
{
if(c == '\r' || c == '\n' || c == 0)
return(c);
str.push_back(c);
}
return(str.length() ? 256 : -1);
}

View File

@ -0,0 +1,89 @@
/* Mednafen - Multi-system Emulator
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __MDFN_FILESTREAM_H
#define __MDFN_FILESTREAM_H
#include "Stream.h"
#include "error.h"
#include <stdio.h>
#include <string>
class FileStream : public Stream
{
public:
enum
{
MODE_READ = 0,
MODE_WRITE,
MODE_WRITE_SAFE, // Will throw an exception instead of overwriting an existing file.
MODE_WRITE_INPLACE, // Like MODE_WRITE, but won't truncate the file if it already exists.
};
FileStream(const std::string& path, const int mode);
virtual ~FileStream() override;
virtual uint64 attributes(void) override;
virtual uint8 *map(void) noexcept override;
virtual uint64 map_size(void) noexcept override;
virtual void unmap(void) noexcept override;
virtual uint64 read(void *data, uint64 count, bool error_on_eos = true) override;
virtual void write(const void *data, uint64 count) override;
virtual void truncate(uint64 length) override;
virtual void seek(int64 offset, int whence) override;
virtual uint64 tell(void) override;
virtual uint64 size(void) override;
virtual void flush(void) override;
virtual void close(void) override;
virtual int get_line(std::string &str) override;
INLINE int get_char(void)
{
int ret;
errno = 0;
ret = fgetc(fp);
if(MDFN_UNLIKELY(errno != 0))
{
ErrnoHolder ene(errno);
throw(MDFN_Error(ene.Errno(), ("Error reading from opened file \"%s\": %s"), path_save.c_str(), ene.StrError()));
}
return(ret);
}
private:
FileStream & operator=(const FileStream &); // Assignment operator
FileStream(const FileStream &); // Copy constructor
//FileStream(FileStream &); // Copy constructor
FILE *fp;
std::string path_save;
const int OpenedMode;
void* mapping;
uint64 mapping_size;
};
#endif

View File

@ -0,0 +1,38 @@
#include "emuware/emuware.h"
#include "Mednadisc.h"
#include "error.h"
#include "cdrom/CDAccess.h"
#include "cdrom/CDUtility.h"
#include "cdrom/cdromif.h"
#include "cdrom/CDAccess_Image.h"
EW_EXPORT void* mednadisc_LoadCD(const char* fname)
{
CDAccess* disc = NULL;
try {
disc = cdaccess_open_image(fname,false);
}
catch(MDFN_Error &e) {
return NULL;
}
return disc;
}
EW_EXPORT int32 mednadisc_ReadSector(CDAccess* disc, int lba, void* buf2448)
{
try {
disc->Read_Raw_Sector((uint8*)buf2448,lba);
}
catch(MDFN_Error &e) {
return 0;
}
return 1;
}
EW_EXPORT void mednadisc_CloseCD(CDAccess* disc)
{
delete disc;
}

View File

@ -0,0 +1,9 @@
#pragma once
#include "emuware/emuware.h"
class CDAccess;
EW_EXPORT void* mednadisc_LoadCD(const char* fname);
EW_EXPORT int32 mednadisc_ReadSector(CDAccess* disc, int lba, void* buf2448);
EW_EXPORT void mednadisc_CloseCD(CDAccess* disc);

View File

@ -0,0 +1,330 @@
/* Mednafen - Multi-system Emulator
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdlib.h>
#include "MemoryStream.h"
#include "math_ops.h"
#include "error.h"
//work around gettext
#define _(X) X
/*
TODO: Copy and assignment constructor fixes.
Proper negative position behavior?
*/
MemoryStream::MemoryStream() : data_buffer(NULL), data_buffer_size(0), data_buffer_alloced(0), position(0)
{
data_buffer_size = 0;
data_buffer_alloced = 64;
if(!(data_buffer = (uint8*)realloc(data_buffer, data_buffer_alloced)))
throw MDFN_Error(ErrnoHolder(errno));
}
MemoryStream::MemoryStream(uint64 alloc_hint, int alloc_hint_is_size) : data_buffer(NULL), data_buffer_size(0), data_buffer_alloced(0), position(0)
{
if(alloc_hint_is_size != 0)
{
data_buffer_size = alloc_hint;
data_buffer_alloced = alloc_hint;
if(alloc_hint > SIZE_MAX)
throw MDFN_Error(ErrnoHolder(ENOMEM));
}
else
{
data_buffer_size = 0;
data_buffer_alloced = (alloc_hint > SIZE_MAX) ? SIZE_MAX : alloc_hint;
}
if(!(data_buffer = (uint8*)realloc(data_buffer, data_buffer_alloced)))
throw MDFN_Error(ErrnoHolder(errno));
if(alloc_hint_is_size > 0)
memset(data_buffer, 0, data_buffer_size);
}
MemoryStream::MemoryStream(Stream *stream, uint64 size_limit) : data_buffer(NULL), data_buffer_size(0), data_buffer_alloced(0), position(0)
{
try
{
if((position = stream->tell()) != 0)
stream->seek(0, SEEK_SET);
void* tp;
data_buffer_size = data_buffer_alloced = stream->alloc_and_read(&tp, size_limit);
data_buffer = (uint8*)tp;
stream->close();
}
catch(...)
{
if(data_buffer)
{
free(data_buffer);
data_buffer = NULL;
}
delete stream;
throw;
}
delete stream;
}
MemoryStream::MemoryStream(const MemoryStream &zs)
{
data_buffer_size = zs.data_buffer_size;
data_buffer_alloced = zs.data_buffer_alloced;
if(!(data_buffer = (uint8*)malloc(data_buffer_alloced)))
throw MDFN_Error(ErrnoHolder(errno));
memcpy(data_buffer, zs.data_buffer, data_buffer_size);
position = zs.position;
}
#if 0
MemoryStream & MemoryStream::operator=(const MemoryStream &zs)
{
if(this != &zs)
{
if(data_buffer)
{
free(data_buffer);
data_buffer = NULL;
}
data_buffer_size = zs.data_buffer_size;
data_buffer_alloced = zs.data_buffer_alloced;
if(!(data_buffer = (uint8*)malloc(data_buffer_alloced)))
throw MDFN_Error(ErrnoHolder(errno));
memcpy(data_buffer, zs.data_buffer, data_buffer_size);
position = zs.position;
}
return(*this);
}
#endif
MemoryStream::~MemoryStream()
{
if(data_buffer)
{
free(data_buffer);
data_buffer = NULL;
}
}
uint64 MemoryStream::attributes(void)
{
return (ATTRIBUTE_READABLE | ATTRIBUTE_WRITEABLE | ATTRIBUTE_SEEKABLE);
}
uint8 *MemoryStream::map(void) noexcept
{
return data_buffer;
}
uint64 MemoryStream::map_size(void) noexcept
{
return data_buffer_size;
}
void MemoryStream::unmap(void) noexcept
{
}
INLINE void MemoryStream::grow_if_necessary(uint64 new_required_size, uint64 hole_end)
{
if(new_required_size > data_buffer_size)
{
const uint64 old_data_buffer_size = data_buffer_size;
if(new_required_size > data_buffer_alloced)
{
uint64 new_required_alloced = round_up_pow2(new_required_size);
uint8 *new_data_buffer;
// first condition will happen at new_required_size > (1ULL << 63) due to round_up_pow2() "wrapping".
// second condition can occur when running on a 32-bit system.
if(new_required_alloced < new_required_size || new_required_alloced > SIZE_MAX)
new_required_alloced = SIZE_MAX;
// If constrained alloc size isn't enough, throw an out-of-memory/address-space type error.
if(new_required_alloced < new_required_size)
throw MDFN_Error(ErrnoHolder(ENOMEM));
if(!(new_data_buffer = (uint8*)realloc(data_buffer, new_required_alloced)))
throw MDFN_Error(ErrnoHolder(errno));
//
// Assign all in one go after the realloc() so we don't leave our object in an inconsistent state if the realloc() fails.
//
data_buffer = new_data_buffer;
data_buffer_size = new_required_size;
data_buffer_alloced = new_required_alloced;
}
else
data_buffer_size = new_required_size;
if(hole_end > old_data_buffer_size)
memset(data_buffer + old_data_buffer_size, 0, hole_end - old_data_buffer_size);
}
}
void MemoryStream::shrink_to_fit(void) noexcept
{
if(data_buffer_alloced > data_buffer_size)
{
uint8 *new_data_buffer;
new_data_buffer = (uint8*)realloc(data_buffer, data_buffer_size);
if(new_data_buffer != NULL)
{
data_buffer = new_data_buffer;
data_buffer_alloced = data_buffer_size;
}
}
}
uint64 MemoryStream::read(void *data, uint64 count, bool error_on_eos)
{
//printf("%llu %llu %llu\n", position, count, data_buffer_size);
if(count > data_buffer_size)
{
if(error_on_eos)
throw MDFN_Error(0, _("Unexpected EOF"));
count = data_buffer_size;
}
if(position > (data_buffer_size - count))
{
if(error_on_eos)
throw MDFN_Error(0, _("Unexpected EOF"));
if(data_buffer_size > position)
count = data_buffer_size - position;
else
count = 0;
}
memmove(data, &data_buffer[position], count);
position += count;
return count;
}
void MemoryStream::write(const void *data, uint64 count)
{
uint64 nrs = position + count;
if(nrs < position)
throw MDFN_Error(ErrnoHolder(EFBIG));
grow_if_necessary(nrs, position);
memmove(&data_buffer[position], data, count);
position += count;
}
//
// Don't add code to reduce the amount of memory allocated(when possible) without providing a
// per-stream setting to disable that behavior.
//
void MemoryStream::truncate(uint64 length)
{
grow_if_necessary(length, length);
data_buffer_size = length;
}
void MemoryStream::seek(int64 offset, int whence)
{
uint64 new_position;
switch(whence)
{
default:
throw MDFN_Error(ErrnoHolder(EINVAL));
break;
case SEEK_SET:
new_position = offset;
break;
case SEEK_CUR:
new_position = position + offset;
break;
case SEEK_END:
new_position = data_buffer_size + offset;
break;
}
if(new_position < 0)
throw MDFN_Error(ErrnoHolder(EINVAL));
position = new_position;
}
uint64 MemoryStream::tell(void)
{
return position;
}
uint64 MemoryStream::size(void)
{
return data_buffer_size;
}
void MemoryStream::flush(void)
{
}
void MemoryStream::close(void)
{
}
int MemoryStream::get_line(std::string &str)
{
str.clear(); // or str.resize(0)??
while((uint64)position < data_buffer_size)
{
uint8 c = data_buffer[position++];
if(c == '\r' || c == '\n' || c == 0)
return(c);
str.push_back(c); // Should be faster than str.append(1, c)
}
return(str.length() ? 256 : -1);
}

View File

@ -0,0 +1,77 @@
/* Mednafen - Multi-system Emulator
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
Notes:
For performance reasons(like in the state rewinding code), we should try to make sure map()
returns a pointer that is aligned to at least what malloc()/realloc() provides.
(And maybe forcefully align it to at least 16 bytes in the future)
*/
#ifndef __MDFN_MEMORYSTREAM_H
#define __MDFN_MEMORYSTREAM_H
#include "Stream.h"
class MemoryStream : public Stream
{
public:
MemoryStream();
MemoryStream(uint64 alloc_hint, int alloc_hint_is_size = false); // Pass -1 instead of 1 for alloc_hint_is_size to skip initialization of the memory.
MemoryStream(Stream *stream, uint64 size_limit = ~(uint64)0);
// Will create a MemoryStream equivalent of the contents of "stream", and then "delete stream".
// Will only work if stream->tell() == 0, or if "stream" is seekable.
// stream will be deleted even if this constructor throws.
//
// Will throw an exception if the initial size() of the MemoryStream would be greater than size_limit(useful for when passing
// in GZFileStream streams).
MemoryStream(const MemoryStream &zs);
MemoryStream & operator=(const MemoryStream &zs);
virtual ~MemoryStream() override;
virtual uint64 attributes(void) override;
virtual uint8 *map(void) noexcept override;
virtual uint64 map_size(void) noexcept override;
virtual void unmap(void) noexcept override;
virtual uint64 read(void *data, uint64 count, bool error_on_eos = true) override;
virtual void write(const void *data, uint64 count) override;
virtual void truncate(uint64 length) override;
virtual void seek(int64 offset, int whence) override;
virtual uint64 tell(void) override;
virtual uint64 size(void) override;
virtual void flush(void) override;
virtual void close(void) override;
virtual int get_line(std::string &str) override;
void shrink_to_fit(void) noexcept; // Minimizes alloced memory.
private:
uint8 *data_buffer;
uint64 data_buffer_size;
uint64 data_buffer_alloced;
uint64 position;
void grow_if_necessary(uint64 new_required_size, uint64 hole_end);
};
#endif

219
psx/mednadisc/Stream.cpp Normal file
View File

@ -0,0 +1,219 @@
/* Mednafen - Multi-system Emulator
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include "emuware/emuware.h"
#include "Stream.h"
#include "error.h"
//work around gettext stuff
#define _(X) X
Stream::Stream()
{
}
Stream::~Stream()
{
}
uint64 Stream::read_discard(uint64 count)
{
uint8 buf[1024];
uint64 tmp;
uint64 ret = 0;
do
{
tmp = read(buf, std::min<uint64>(count, sizeof(buf)), false);
count -= tmp;
ret += tmp;
} while(tmp == sizeof(buf));
return ret;
}
uint64 Stream::alloc_and_read(void** data_out, uint64 size_limit)
{
uint8 *data_buffer = NULL;
uint64 data_buffer_size = 0;
uint64 data_buffer_alloced = 0;
try
{
if(attributes() & ATTRIBUTE_SLOW_SIZE)
{
uint64 rti;
data_buffer_size = 0;
data_buffer_alloced = 65536;
if(!(data_buffer = (uint8*)realloc(data_buffer, data_buffer_alloced)))
throw MDFN_Error(ErrnoHolder(errno));
while((rti = read(data_buffer + data_buffer_size, data_buffer_alloced - data_buffer_size, false)) > 0)
{
uint8* new_data_buffer;
data_buffer_size += rti;
if(data_buffer_size == data_buffer_alloced)
{
data_buffer_alloced <<= 1;
if(data_buffer_alloced > size_limit) // So we can test against our size limit without going far far over it in temporary memory allocations.
data_buffer_alloced = size_limit + 1;
if(data_buffer_size > size_limit)
throw MDFN_Error(0, _("Size limit of %llu bytes would be exceeded."), (unsigned long long)size_limit);
if(!(new_data_buffer = (uint8 *)realloc(data_buffer, data_buffer_alloced)))
throw MDFN_Error(ErrnoHolder(errno));
data_buffer = new_data_buffer;
}
else // EOS
break;
}
if(data_buffer_alloced > data_buffer_size)
{
uint8 *new_data_buffer;
new_data_buffer = (uint8*)realloc(data_buffer, data_buffer_size);
if(new_data_buffer != NULL)
{
data_buffer = new_data_buffer;
data_buffer_alloced = data_buffer_size;
}
}
}
else
{
data_buffer_size = size();
data_buffer_alloced = data_buffer_size;
if(data_buffer_size > size_limit)
throw MDFN_Error(0, _("Size limit of %llu bytes would be exceeded."), (unsigned long long)size_limit);
if(data_buffer_alloced > SIZE_MAX)
throw MDFN_Error(ErrnoHolder(ENOMEM));
if(!(data_buffer = (uint8*)realloc(data_buffer, data_buffer_alloced)))
throw MDFN_Error(ErrnoHolder(errno));
read(data_buffer, data_buffer_size);
}
}
catch(...)
{
if(data_buffer)
{
free(data_buffer);
data_buffer = NULL;
}
throw;
}
*data_out = data_buffer;
return data_buffer_size;
}
uint8* Stream::map(void) noexcept
{
return(NULL);
}
uint64 Stream::map_size(void) noexcept
{
return 0;
}
void Stream::unmap(void) noexcept
{
}
void Stream::put_line(const std::string& str)
{
char l = '\n';
write(&str[0], str.size());
write(&l, sizeof(l));
}
void Stream::print_format(const char *format, ...)
{
char *str = NULL;
int rc;
va_list ap;
int size = 128;
for(;;) {
va_list ap;
va_start(ap, format);
str = (char*)malloc(size);
size *= 2;
int ret = vsprintf(str, format, ap);
va_end(ap);
if(ret>=0)
break;
free(str);
}
if(rc < 0)
throw MDFN_Error(0, "Error in trio_vasprintf()");
else
{
try // Bleck
{
write(str, rc);
}
catch(...)
{
free(str);
throw;
}
free(str);
}
}
int Stream::get_line(std::string &str)
{
uint8 c;
str.clear(); // or str.resize(0)??
while(read(&c, sizeof(c), false) > 0)
{
if(c == '\r' || c == '\n' || c == 0)
return(c);
str.push_back(c);
}
return(str.length() ? 256 : -1);
}

223
psx/mednadisc/Stream.h Normal file
View File

@ -0,0 +1,223 @@
#ifndef __MDFN_STREAM_H
#define __MDFN_STREAM_H
// TODO?: BufferedStream, no virtual functions, yes inline functions, constructor takes a Stream* argument.
#include "emuware/emuware.h"
#include <errno.h>
#include <stdio.h> // For SEEK_* defines, which we will use in Stream out of FORCE OF HABIT.
#include <string.h>
#include <string>
class Stream
{
public:
Stream();
virtual ~Stream();
enum
{
ATTRIBUTE_READABLE = 1U << 0,
ATTRIBUTE_WRITEABLE = 1U << 1,
ATTRIBUTE_SEEKABLE = 1U << 2,
ATTRIBUTE_SLOW_SEEK = 1U << 3,
ATTRIBUTE_SLOW_SIZE = 1U << 4
};
virtual uint64 attributes(void) = 0;
virtual uint8 *map(void) noexcept;
// Map the entirety of the stream data into the address space of the process, if possible, and return a pointer.
// (the returned pointer must be cached, and returned on any subsequent calls to map() without an unmap()
// in-between, to facilitate a sort of "feature-testing", to determine if an alternative like "MemoryStream"
// should be used).
//
// If the mapping fails for whatever reason, return NULL rather than throwing an exception.
//
// For code using this functionality, ensure usage of map_size() instead of size(), unless you're only using a specific derived
// class like MemoryStream() where the value returned by size() won't change unexpectedly due to outside factors.
virtual uint64 map_size(void) noexcept;
// The size of the memory mapping area, point to which returned by map().
//
// Returns 0 on supported, or if no mapping currently exists.
virtual void unmap(void) noexcept;
// Unmap the stream data from the address space. (Possibly invalidating the pointer returned from map()).
// (must automatically be called, if necessary, from the destructor).
//
// If the data can't be "unmapped" as such because it was never mmap()'d or similar in the first place(such as with MemoryStream),
// then this will be a nop.
virtual uint64 read(void *data, uint64 count, bool error_on_eos = true) = 0;
virtual void write(const void *data, uint64 count) = 0;
virtual void truncate(uint64 length) = 0; // Should have ftruncate()-like semantics; but avoid using it to extend files.
virtual void seek(int64 offset, int whence = SEEK_SET) = 0;
inline void rewind(void)
{
seek(0, SEEK_SET);
}
virtual uint64 tell(void) = 0;
virtual uint64 size(void) = 0; // May implicitly call flush() if the stream is writeable.
virtual void flush(void) = 0;
virtual void close(void) = 0; // Flushes(in the case of writeable streams) and closes the stream.
// Necessary since this operation can fail(running out of disk space, for instance),
// and throw an exception in the destructor would be a Bad Idea(TM).
//
// Manually calling this function isn't strictly necessary, but recommended when the
// stream is writeable; it will be called automatically from the destructor, with any
// exceptions thrown caught and logged.
//
// Utility functions(TODO):
//
INLINE uint8 get_u8(void)
{
uint8 ret;
read(&ret, sizeof(ret));
return ret;
}
INLINE void put_u8(uint8 c)
{
write(&c, sizeof(c));
}
template<typename T>
INLINE T get_NE(void)
{
T ret;
read(&ret, sizeof(ret));
return ret;
}
template<typename T>
INLINE T get_RE(void)
{
uint8 tmp[sizeof(T)];
union
{
T ret;
uint8 ret_u8[sizeof(T)];
};
read(tmp, sizeof(tmp));
for(unsigned i = 0; i < sizeof(T); i++)
ret_u8[i] = tmp[sizeof(T) - 1 - i];
return ret;
}
template<typename T>
INLINE void put_NE(T c)
{
write(&c, sizeof(c));
}
template<typename T>
INLINE void put_RE(T c)
{
uint8 tmp[sizeof(T)];
for(unsigned i = 0; i < sizeof(T); i++)
tmp[i] = ((uint8 *)&c)[sizeof(T) - 1 - i];
write(tmp, sizeof(tmp));
}
template<typename T>
INLINE T get_LE(void)
{
#ifdef LSB_FIRST
return get_NE<T>();
#else
return get_RE<T>();
#endif
}
template<typename T>
INLINE void put_LE(T c)
{
#ifdef LSB_FIRST
return put_NE<T>(c);
#else
return put_RE<T>(c);
#endif
}
template<typename T>
INLINE T get_BE(void)
{
#ifndef LSB_FIRST
return get_NE<T>();
#else
return get_RE<T>();
#endif
}
template<typename T>
INLINE void put_BE(T c)
{
#ifndef LSB_FIRST
return put_NE<T>(c);
#else
return put_RE<T>(c);
#endif
}
INLINE void put_string(const char* str)
{
write(str, strlen(str));
}
// Reads a line into "str", overwriting its contents; returns the line-end char('\n' or '\r' or '\0'), or 256 on EOF and
// data has been read into "str", and -1 on EOF when no data has been read into "str".
// The line-end char won't be added to "str".
// It's up to the caller to handle extraneous empty lines caused by DOS-format text lines(\r\n).
// ("str" is passed by reference for the possibility of improved performance by reusing alloced memory for the std::string, though part
// of it would be up to the STL implementation).
// Implemented as virtual so that a higher-performance version can be implemented if possible(IE with MemoryStream)
virtual int get_line(std::string &str);
virtual void put_line(const std::string& str);
virtual void print_format(const char *format, ...) MDFN_FORMATSTR(gnu_printf, 2, 3);
#if 0
int scanf(const char *format, ...) MDFN_FORMATSTR(gnu_scanf, 2, 3);
void put_string(const char *str);
void put_string(const std::string &str);
#endif
//
// Read until end-of-stream(or count), discarding any read data, and returns the amount of data "read".
// (Useful for detecting and printing warnings about extra garbage data without needing to call size(),
// which can be problematic for some types of Streams).
uint64 read_discard(uint64 count = ~(uint64)0);
//
// Reads stream starting at the current stream position(as returned by tell()), into memory allocated with malloc() and realloc(), and
// sets *data_out to a pointer to the memory(which the caller will need to free() at some point).
//
// *data_out is only an output.
//
// If size_limit is/will be exceeded, an exception will be thrown, and *data_out will not be written to.
//
// Will return the amount of data read(and the size of the alloced memory).
//
uint64 alloc_and_read(void** data_out, uint64 size_limit = ~(uint64)0);
};
#endif

View File

@ -0,0 +1,608 @@
/* Mednafen - Multi-system Emulator
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
// AR_Open(), and AudioReader, will NOT take "ownership" of the Stream object(IE it won't ever delete it). Though it does assume it has exclusive access
// to it for as long as the AudioReader object exists.
// Don't allow exceptions to propagate into the vorbis/musepack/etc. libraries, as it could easily leave the state of the library's decoder "object" in an
// inconsistent state, which would cause all sorts of unfun when we try to destroy it while handling the exception farther up.
#include "../mednafen.h"
#include "audioreader.h"
#include <sys/types.h>
#include <sys/stat.h>
#include "../tremor/ivorbisfile.h"
#include "../mpcdec/mpcdec.h"
#ifdef HAVE_LIBSNDFILE
#include <sndfile.h>
#endif
#ifdef HAVE_OPUSFILE
#include "audioreader_opus.h"
#endif
#include <string.h>
#include <errno.h>
#include <time.h>
#include "../general.h"
#include "../endian.h"
AudioReader::AudioReader() : LastReadPos(0)
{
}
AudioReader::~AudioReader()
{
}
int64 AudioReader::Read_(int16 *buffer, int64 frames)
{
abort();
return(false);
}
bool AudioReader::Seek_(int64 frame_offset)
{
abort();
return(false);
}
int64 AudioReader::FrameCount(void)
{
abort();
return(0);
}
/*
**
**
**
**
**
**
**
**
**
*/
class OggVorbisReader : public AudioReader
{
public:
OggVorbisReader(Stream *fp);
~OggVorbisReader();
int64 Read_(int16 *buffer, int64 frames);
bool Seek_(int64 frame_offset);
int64 FrameCount(void);
private:
OggVorbis_File ovfile;
Stream *fw;
};
static size_t iov_read_func(void *ptr, size_t size, size_t nmemb, void *user_data)
{
Stream *fw = (Stream*)user_data;
if(!size)
return(0);
try
{
return fw->read(ptr, size * nmemb, false) / size;
}
catch(...)
{
return(0);
}
}
static int iov_seek_func(void *user_data, ogg_int64_t offset, int whence)
{
Stream *fw = (Stream*)user_data;
try
{
fw->seek(offset, whence);
return(0);
}
catch(...)
{
return(-1);
}
}
static int iov_close_func(void *user_data)
{
Stream *fw = (Stream*)user_data;
try
{
fw->close();
return(0);
}
catch(...)
{
return EOF;
}
}
static long iov_tell_func(void *user_data)
{
Stream *fw = (Stream*)user_data;
try
{
return fw->tell();
}
catch(...)
{
return(-1);
}
}
OggVorbisReader::OggVorbisReader(Stream *fp) : fw(fp)
{
ov_callbacks cb;
memset(&cb, 0, sizeof(cb));
cb.read_func = iov_read_func;
cb.seek_func = iov_seek_func;
cb.close_func = iov_close_func;
cb.tell_func = iov_tell_func;
fp->seek(0, SEEK_SET);
if(ov_open_callbacks(fp, &ovfile, NULL, 0, cb))
throw(0);
}
OggVorbisReader::~OggVorbisReader()
{
ov_clear(&ovfile);
}
int64 OggVorbisReader::Read_(int16 *buffer, int64 frames)
{
uint8 *tw_buf = (uint8 *)buffer;
int cursection = 0;
long toread = frames * sizeof(int16) * 2;
while(toread > 0)
{
long didread = ov_read(&ovfile, (char*)tw_buf, toread, &cursection);
if(didread == 0)
break;
tw_buf = (uint8 *)tw_buf + didread;
toread -= didread;
}
return(frames - toread / sizeof(int16) / 2);
}
bool OggVorbisReader::Seek_(int64 frame_offset)
{
ov_pcm_seek(&ovfile, frame_offset);
return(true);
}
int64 OggVorbisReader::FrameCount(void)
{
return(ov_pcm_total(&ovfile, -1));
}
class MPCReader : public AudioReader
{
public:
MPCReader(Stream *fp);
~MPCReader();
int64 Read_(int16 *buffer, int64 frames);
bool Seek_(int64 frame_offset);
int64 FrameCount(void);
private:
mpc_reader reader;
mpc_demux *demux;
mpc_streaminfo si;
MPC_SAMPLE_FORMAT MPCBuffer[MPC_DECODER_BUFFER_LENGTH];
uint32 MPCBufferIn;
uint32 MPCBufferOffs;
Stream *fw;
};
/// Reads size bytes of data into buffer at ptr.
static mpc_int32_t impc_read(mpc_reader *p_reader, void *ptr, mpc_int32_t size)
{
Stream *fw = (Stream*)(p_reader->data);
try
{
return fw->read(ptr, size, false);
}
catch(...)
{
return(MPC_STATUS_FAIL);
}
}
/// Seeks to byte position offset.
static mpc_bool_t impc_seek(mpc_reader *p_reader, mpc_int32_t offset)
{
Stream *fw = (Stream*)(p_reader->data);
try
{
fw->seek(offset, SEEK_SET);
return(MPC_TRUE);
}
catch(...)
{
return(MPC_FALSE);
}
}
/// Returns the current byte offset in the stream.
static mpc_int32_t impc_tell(mpc_reader *p_reader)
{
Stream *fw = (Stream*)(p_reader->data);
try
{
return fw->tell();
}
catch(...)
{
return(MPC_STATUS_FAIL);
}
}
/// Returns the total length of the source stream, in bytes.
static mpc_int32_t impc_get_size(mpc_reader *p_reader)
{
Stream *fw = (Stream*)(p_reader->data);
try
{
return fw->size();
}
catch(...)
{
return(MPC_STATUS_FAIL);
}
}
/// True if the stream is a seekable stream.
static mpc_bool_t impc_canseek(mpc_reader *p_reader)
{
return(MPC_TRUE);
}
MPCReader::MPCReader(Stream *fp) : fw(fp)
{
fp->seek(0, SEEK_SET);
demux = NULL;
memset(&si, 0, sizeof(si));
memset(MPCBuffer, 0, sizeof(MPCBuffer));
MPCBufferOffs = 0;
MPCBufferIn = 0;
memset(&reader, 0, sizeof(reader));
reader.read = impc_read;
reader.seek = impc_seek;
reader.tell = impc_tell;
reader.get_size = impc_get_size;
reader.canseek = impc_canseek;
reader.data = (void*)fp;
if(!(demux = mpc_demux_init(&reader)))
{
throw(0);
}
mpc_demux_get_info(demux, &si);
if(si.channels != 2)
{
mpc_demux_exit(demux);
demux = NULL;
throw MDFN_Error(0, _("MusePack stream has wrong number of channels(%u); the correct number is 2."), si.channels);
}
if(si.sample_freq != 44100)
{
mpc_demux_exit(demux);
demux = NULL;
throw MDFN_Error(0, _("MusePack stream has wrong samplerate(%u Hz); the correct samplerate is 44100 Hz."), si.sample_freq);
}
}
MPCReader::~MPCReader()
{
if(demux)
{
mpc_demux_exit(demux);
demux = NULL;
}
}
int64 MPCReader::Read_(int16 *buffer, int64 frames)
{
mpc_status err;
int16 *cowbuf = (int16 *)buffer;
int32 toread = frames * 2;
while(toread > 0)
{
int32 tmplen;
if(!MPCBufferIn)
{
mpc_frame_info fi;
memset(&fi, 0, sizeof(fi));
fi.buffer = MPCBuffer;
if((err = mpc_demux_decode(demux, &fi)) < 0 || fi.bits == -1)
return(frames - toread / 2);
MPCBufferIn = fi.samples * 2;
MPCBufferOffs = 0;
}
tmplen = MPCBufferIn;
if(tmplen >= toread)
tmplen = toread;
for(int x = 0; x < tmplen; x++)
{
#ifdef MPC_FIXED_POINT
int32 samp = MPCBuffer[MPCBufferOffs + x] >> MPC_FIXED_POINT_FRACTPART;
#else
#warning Floating-point MPC decoding path not tested.
int32 samp = (int32)(MPCBuffer[MPCBufferOffs + x] * 32767);
#endif
if(samp < -32768)
samp = -32768;
if(samp > 32767)
samp = 32767;
*cowbuf = (int16)samp;
cowbuf++;
}
MPCBufferOffs += tmplen;
toread -= tmplen;
MPCBufferIn -= tmplen;
}
return(frames - toread / 2);
}
bool MPCReader::Seek_(int64 frame_offset)
{
MPCBufferOffs = 0;
MPCBufferIn = 0;
if(mpc_demux_seek_sample(demux, frame_offset) < 0)
return(false);
return(true);
}
int64 MPCReader::FrameCount(void)
{
return(mpc_streaminfo_get_length_samples(&si));
}
/*
**
**
**
**
**
**
**
**
**
*/
#ifdef HAVE_LIBSNDFILE
class SFReader : public AudioReader
{
public:
SFReader(Stream *fp);
~SFReader();
int64 Read_(int16 *buffer, int64 frames);
bool Seek_(int64 frame_offset);
int64 FrameCount(void);
private:
SNDFILE *sf;
SF_INFO sfinfo;
SF_VIRTUAL_IO sfvf;
Stream *fw;
};
static sf_count_t isf_get_filelen(void *user_data)
{
Stream *fw = (Stream*)user_data;
try
{
return fw->size();
}
catch(...)
{
return(-1);
}
}
static sf_count_t isf_seek(sf_count_t offset, int whence, void *user_data)
{
Stream *fw = (Stream*)user_data;
try
{
//printf("Seek: offset=%lld, whence=%lld\n", (long long)offset, (long long)whence);
fw->seek(offset, whence);
return fw->tell();
}
catch(...)
{
//printf(" SEEK FAILED\n");
return(-1);
}
}
static sf_count_t isf_read(void *ptr, sf_count_t count, void *user_data)
{
Stream *fw = (Stream*)user_data;
try
{
sf_count_t ret = fw->read(ptr, count, false);
//printf("Read: count=%lld, ret=%lld\n", (long long)count, (long long)ret);
return ret;
}
catch(...)
{
//printf(" READ FAILED\n");
return(0);
}
}
static sf_count_t isf_write(const void *ptr, sf_count_t count, void *user_data)
{
return(0);
}
static sf_count_t isf_tell(void *user_data)
{
Stream *fw = (Stream*)user_data;
try
{
return fw->tell();
}
catch(...)
{
return(-1);
}
}
SFReader::SFReader(Stream *fp) : fw(fp)
{
fp->seek(0, SEEK_SET);
memset(&sfvf, 0, sizeof(sfvf));
sfvf.get_filelen = isf_get_filelen;
sfvf.seek = isf_seek;
sfvf.read = isf_read;
sfvf.write = isf_write;
sfvf.tell = isf_tell;
memset(&sfinfo, 0, sizeof(sfinfo));
if(!(sf = sf_open_virtual(&sfvf, SFM_READ, &sfinfo, (void*)fp)))
throw(0);
}
SFReader::~SFReader()
{
sf_close(sf);
}
int64 SFReader::Read_(int16 *buffer, int64 frames)
{
return(sf_read_short(sf, (short*)buffer, frames * 2) / 2);
}
bool SFReader::Seek_(int64 frame_offset)
{
// FIXME error condition
if(sf_seek(sf, frame_offset, SEEK_SET) != frame_offset)
return(false);
return(true);
}
int64 SFReader::FrameCount(void)
{
return(sfinfo.frames);
}
#endif
AudioReader *AR_Open(Stream *fp)
{
try
{
return new MPCReader(fp);
}
catch(int i)
{
}
#ifdef HAVE_OPUSFILE
try
{
return new OpusReader(fp);
}
catch(int i)
{
}
#endif
try
{
return new OggVorbisReader(fp);
}
catch(int i)
{
}
#ifdef HAVE_LIBSNDFILE
try
{
return new SFReader(fp);
}
catch(int i)
{
}
#endif
return(NULL);
}

View File

@ -0,0 +1,43 @@
#ifndef __MDFN_AUDIOREADER_H
#define __MDFN_AUDIOREADER_H
#include "../Stream.h"
class AudioReader
{
public:
AudioReader();
virtual ~AudioReader();
virtual int64 FrameCount(void);
INLINE int64 Read(int64 frame_offset, int16 *buffer, int64 frames)
{
int64 ret;
//if(frame_offset >= 0)
{
if(LastReadPos != frame_offset)
{
//puts("SEEK");
if(!Seek_(frame_offset))
return(0);
LastReadPos = frame_offset;
}
}
ret = Read_(buffer, frames);
LastReadPos += ret;
return(ret);
}
private:
virtual int64 Read_(int16 *buffer, int64 frames);
virtual bool Seek_(int64 frame_offset);
int64 LastReadPos;
};
// AR_Open(), and AudioReader, will NOT take "ownership" of the Stream object(IE it won't ever delete it). Though it does assume it has exclusive access
// to it for as long as the AudioReader object exists.
AudioReader *AR_Open(Stream *fp);
#endif

View File

@ -0,0 +1,248 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="psx">
<UniqueIdentifier>{00f73db4-1182-4bf7-b891-66bf860d3742}</UniqueIdentifier>
</Filter>
<Filter Include="emuware">
<UniqueIdentifier>{f69cc8f2-7480-44d6-9a32-9dca789d2bf6}</UniqueIdentifier>
</Filter>
<Filter Include="cdrom">
<UniqueIdentifier>{57a8e6ec-9225-410d-b38f-ba209abae070}</UniqueIdentifier>
</Filter>
<Filter Include="psx\input">
<UniqueIdentifier>{76abb796-5411-4d33-b3e0-f1f3873f138e}</UniqueIdentifier>
</Filter>
<Filter Include="emuware\msvc">
<UniqueIdentifier>{cb700979-4dce-4b10-8521-3ab71a313271}</UniqueIdentifier>
</Filter>
<Filter Include="video">
<UniqueIdentifier>{d1f71901-17a5-441a-8b4f-f7da34a057c1}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\psx\cdc.cpp">
<Filter>psx</Filter>
</ClCompile>
<ClCompile Include="..\psx\psx.cpp">
<Filter>psx</Filter>
</ClCompile>
<ClCompile Include="..\psx\dis.cpp">
<Filter>psx</Filter>
</ClCompile>
<ClCompile Include="..\psx\gte.cpp">
<Filter>psx</Filter>
</ClCompile>
<ClCompile Include="..\psx\spu.cpp">
<Filter>psx</Filter>
</ClCompile>
<ClCompile Include="..\Stream.cpp" />
<ClCompile Include="..\psx\frontio.cpp">
<Filter>psx</Filter>
</ClCompile>
<ClCompile Include="..\psx\cpu.cpp">
<Filter>psx</Filter>
</ClCompile>
<ClCompile Include="..\psx\dma.cpp">
<Filter>psx</Filter>
</ClCompile>
<ClCompile Include="..\psx\irq.cpp">
<Filter>psx</Filter>
</ClCompile>
<ClCompile Include="..\psx\mdec.cpp">
<Filter>psx</Filter>
</ClCompile>
<ClCompile Include="..\psx\sio.cpp">
<Filter>psx</Filter>
</ClCompile>
<ClCompile Include="..\psx\timer.cpp">
<Filter>psx</Filter>
</ClCompile>
<ClCompile Include="..\endian.cpp" />
<ClCompile Include="..\psx\input\dualshock.cpp">
<Filter>psx\input</Filter>
</ClCompile>
<ClCompile Include="..\psx\input\gamepad.cpp">
<Filter>psx\input</Filter>
</ClCompile>
<ClCompile Include="..\psx\input\guncon.cpp">
<Filter>psx\input</Filter>
</ClCompile>
<ClCompile Include="..\psx\input\justifier.cpp">
<Filter>psx\input</Filter>
</ClCompile>
<ClCompile Include="..\psx\input\memcard.cpp">
<Filter>psx\input</Filter>
</ClCompile>
<ClCompile Include="..\psx\input\mouse.cpp">
<Filter>psx\input</Filter>
</ClCompile>
<ClCompile Include="..\psx\input\multitap.cpp">
<Filter>psx\input</Filter>
</ClCompile>
<ClCompile Include="..\psx\input\negcon.cpp">
<Filter>psx\input</Filter>
</ClCompile>
<ClCompile Include="..\psx\input\dualanalog.cpp">
<Filter>psx\input</Filter>
</ClCompile>
<ClCompile Include="..\octoshock.cpp" />
<ClCompile Include="..\emuware\emuware.cpp">
<Filter>emuware</Filter>
</ClCompile>
<ClCompile Include="..\video\surface.cpp">
<Filter>video</Filter>
</ClCompile>
<ClCompile Include="..\video\Deinterlacer.cpp">
<Filter>video</Filter>
</ClCompile>
<ClCompile Include="..\emuware\EW_state.cpp">
<Filter>emuware</Filter>
</ClCompile>
<ClCompile Include="..\psx\gpu.cpp">
<Filter>psx</Filter>
</ClCompile>
<ClCompile Include="..\psx\gpu_line.cpp">
<Filter>psx</Filter>
</ClCompile>
<ClCompile Include="..\psx\gpu_polygon.cpp">
<Filter>psx</Filter>
</ClCompile>
<ClCompile Include="..\psx\gpu_sprite.cpp">
<Filter>psx</Filter>
</ClCompile>
<ClCompile Include="..\cdrom\CDUtility.cpp">
<Filter>cdrom</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\psx\cdc.h">
<Filter>psx</Filter>
</ClInclude>
<ClInclude Include="..\psx\psx.h">
<Filter>psx</Filter>
</ClInclude>
<ClInclude Include="..\emuware\emuware.h">
<Filter>emuware</Filter>
</ClInclude>
<ClInclude Include="..\psx\dis.h">
<Filter>psx</Filter>
</ClInclude>
<ClInclude Include="..\psx\gte.h">
<Filter>psx</Filter>
</ClInclude>
<ClInclude Include="..\math_ops.h" />
<ClInclude Include="..\git.h" />
<ClInclude Include="..\octoshock.h" />
<ClInclude Include="..\psx\spu.h">
<Filter>psx</Filter>
</ClInclude>
<ClInclude Include="..\endian.h" />
<ClInclude Include="..\Stream.h" />
<ClInclude Include="..\error.h" />
<ClInclude Include="..\psx\frontio.h">
<Filter>psx</Filter>
</ClInclude>
<ClInclude Include="..\psx\cpu.h">
<Filter>psx</Filter>
</ClInclude>
<ClInclude Include="..\psx\dma.h">
<Filter>psx</Filter>
</ClInclude>
<ClInclude Include="..\psx\irq.h">
<Filter>psx</Filter>
</ClInclude>
<ClInclude Include="..\psx\mdec.h">
<Filter>psx</Filter>
</ClInclude>
<ClInclude Include="..\psx\sio.h">
<Filter>psx</Filter>
</ClInclude>
<ClInclude Include="..\psx\timer.h">
<Filter>psx</Filter>
</ClInclude>
<ClInclude Include="..\psx\input\dualanalog.h">
<Filter>psx\input</Filter>
</ClInclude>
<ClInclude Include="..\psx\input\dualshock.h">
<Filter>psx\input</Filter>
</ClInclude>
<ClInclude Include="..\psx\input\gamepad.h">
<Filter>psx\input</Filter>
</ClInclude>
<ClInclude Include="..\psx\input\guncon.h">
<Filter>psx\input</Filter>
</ClInclude>
<ClInclude Include="..\psx\input\justifier.h">
<Filter>psx\input</Filter>
</ClInclude>
<ClInclude Include="..\psx\input\memcard.h">
<Filter>psx\input</Filter>
</ClInclude>
<ClInclude Include="..\psx\input\mouse.h">
<Filter>psx\input</Filter>
</ClInclude>
<ClInclude Include="..\psx\input\multitap.h">
<Filter>psx\input</Filter>
</ClInclude>
<ClInclude Include="..\psx\input\negcon.h">
<Filter>psx\input</Filter>
</ClInclude>
<ClInclude Include="..\emuware\msvc\inttypes.h">
<Filter>emuware\msvc</Filter>
</ClInclude>
<ClInclude Include="..\emuware\msvc\stdint.h">
<Filter>emuware\msvc</Filter>
</ClInclude>
<ClInclude Include="..\video\surface.h">
<Filter>video</Filter>
</ClInclude>
<ClInclude Include="..\video\Deinterlacer.h">
<Filter>video</Filter>
</ClInclude>
<ClInclude Include="..\emuware\EW_state.h">
<Filter>emuware</Filter>
</ClInclude>
<ClInclude Include="..\cdrom\SimpleFIFO.h">
<Filter>cdrom</Filter>
</ClInclude>
<ClInclude Include="..\psx\gpu.h">
<Filter>psx</Filter>
</ClInclude>
<ClInclude Include="..\psx\masmem.h">
<Filter>psx</Filter>
</ClInclude>
<ClInclude Include="..\cdrom\CDUtility.h">
<Filter>cdrom</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="..\psx\spu_fir_table.inc">
<Filter>psx</Filter>
</None>
<None Include="..\psx\spu_reverb.inc">
<Filter>psx</Filter>
</None>
<None Include="..\psx\gpu_command_table.inc">
<Filter>psx</Filter>
</None>
<None Include="..\psx\gpu_common.inc">
<Filter>psx</Filter>
</None>
<None Include="..\psx\gpu_line.inc">
<Filter>psx</Filter>
</None>
<None Include="..\psx\gpu_polygon.inc">
<Filter>psx</Filter>
</None>
<None Include="..\psx\gpu_sprite.inc">
<Filter>psx</Filter>
</None>
<None Include="..\psx\cpu_computedgoto.inc">
<Filter>psx</Filter>
</None>
<None Include="..\psx\cpu_bigswitch.inc">
<Filter>psx</Filter>
</None>
</ItemGroup>
</Project>

View File

@ -0,0 +1,24 @@

Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mednadisc", "mednadisc.vcxproj", "{5F35CAFC-6208-4FBE-AD17-0E69BA3F70EC}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Release|Win32 = Release|Win32
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{5F35CAFC-6208-4FBE-AD17-0E69BA3F70EC}.Debug|Win32.ActiveCfg = Debug|Win32
{5F35CAFC-6208-4FBE-AD17-0E69BA3F70EC}.Debug|Win32.Build.0 = Debug|Win32
{5F35CAFC-6208-4FBE-AD17-0E69BA3F70EC}.Release|Win32.ActiveCfg = Release|Win32
{5F35CAFC-6208-4FBE-AD17-0E69BA3F70EC}.Release|Win32.Build.0 = Release|Win32
{5A0DAC84-1170-4B1A-B9A9-F566A1D97790}.Debug|Win32.ActiveCfg = Debug|Win32
{5A0DAC84-1170-4B1A-B9A9-F566A1D97790}.Debug|Win32.Build.0 = Debug|Win32
{5A0DAC84-1170-4B1A-B9A9-F566A1D97790}.Release|Win32.ActiveCfg = Release|Win32
{5A0DAC84-1170-4B1A-B9A9-F566A1D97790}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,134 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\cdrom\audioreader.cpp" />
<ClCompile Include="..\cdrom\CDAccess.cpp" />
<ClCompile Include="..\cdrom\CDAccess_CCD.cpp" />
<ClCompile Include="..\cdrom\CDAccess_Image.cpp" />
<ClCompile Include="..\cdrom\cdromif.cpp" />
<ClCompile Include="..\cdrom\CDUtility.cpp" />
<ClCompile Include="..\cdrom\crc32.cpp" />
<ClCompile Include="..\cdrom\galois.cpp" />
<ClCompile Include="..\cdrom\l-ec.cpp" />
<ClCompile Include="..\cdrom\lec.cpp" />
<ClCompile Include="..\cdrom\recover-raw.cpp" />
<ClCompile Include="..\endian.cpp" />
<ClCompile Include="..\error.cpp" />
<ClCompile Include="..\FileStream.cpp" />
<ClCompile Include="..\general.cpp" />
<ClCompile Include="..\Mednadisc.cpp" />
<ClCompile Include="..\MemoryStream.cpp" />
<ClCompile Include="..\Stream.cpp" />
<ClCompile Include="..\string\trim.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\cdrom\audioreader.h" />
<ClInclude Include="..\cdrom\CDAccess.h" />
<ClInclude Include="..\cdrom\CDAccess_CCD.h" />
<ClInclude Include="..\cdrom\CDAccess_Image.h" />
<ClInclude Include="..\cdrom\cdromif.h" />
<ClInclude Include="..\cdrom\CDUtility.h" />
<ClInclude Include="..\cdrom\dvdisaster.h" />
<ClInclude Include="..\cdrom\galois-inlines.h" />
<ClInclude Include="..\cdrom\lec.h" />
<ClInclude Include="..\cdrom\SimpleFIFO.h" />
<ClInclude Include="..\emuware\emuware.h" />
<ClInclude Include="..\endian.h" />
<ClInclude Include="..\error.h" />
<ClInclude Include="..\FileStream.h" />
<ClInclude Include="..\general.h" />
<ClInclude Include="..\Mednadisc.h" />
<ClInclude Include="..\MemoryStream.h" />
<ClInclude Include="..\Stream.h" />
<ClInclude Include="..\string\trim.h" />
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{5F35CAFC-6208-4FBE-AD17-0E69BA3F70EC}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>mednadisc</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(ProjectDir)\..\..\..\output\dll\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(ProjectDir)\..\..\..\output\dll\</OutDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>EW_EXPORT;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_WINDOWS;_USRDLL;OCTOSHOCK_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>../emuware/msvc;..</AdditionalIncludeDirectories>
<PrecompiledHeaderFile>
</PrecompiledHeaderFile>
<PrecompiledHeaderOutputFile>
</PrecompiledHeaderOutputFile>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<MinimalRebuild>false</MinimalRebuild>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;EW_EXPORT;WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeaderFile>
</PrecompiledHeaderFile>
<PrecompiledHeaderOutputFile>
</PrecompiledHeaderOutputFile>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<AdditionalIncludeDirectories>../emuware/msvc;..</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -0,0 +1,104 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="cdrom">
<UniqueIdentifier>{a3ffd332-9644-473f-b3b6-d31b08be5256}</UniqueIdentifier>
</Filter>
<Filter Include="emuware">
<UniqueIdentifier>{99e57b88-966c-4695-8f5c-1db6345b1b26}</UniqueIdentifier>
</Filter>
<Filter Include="string">
<UniqueIdentifier>{798fa5bd-6381-487a-99d2-35a15a6da439}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\cdrom\CDAccess_Image.cpp">
<Filter>cdrom</Filter>
</ClCompile>
<ClCompile Include="..\cdrom\cdromif.cpp">
<Filter>cdrom</Filter>
</ClCompile>
<ClCompile Include="..\cdrom\CDUtility.cpp">
<Filter>cdrom</Filter>
</ClCompile>
<ClCompile Include="..\cdrom\crc32.cpp">
<Filter>cdrom</Filter>
</ClCompile>
<ClCompile Include="..\cdrom\galois.cpp">
<Filter>cdrom</Filter>
</ClCompile>
<ClCompile Include="..\cdrom\lec.cpp">
<Filter>cdrom</Filter>
</ClCompile>
<ClCompile Include="..\cdrom\l-ec.cpp">
<Filter>cdrom</Filter>
</ClCompile>
<ClCompile Include="..\cdrom\recover-raw.cpp">
<Filter>cdrom</Filter>
</ClCompile>
<ClCompile Include="..\cdrom\CDAccess.cpp">
<Filter>cdrom</Filter>
</ClCompile>
<ClCompile Include="..\cdrom\CDAccess_CCD.cpp">
<Filter>cdrom</Filter>
</ClCompile>
<ClCompile Include="..\Stream.cpp" />
<ClCompile Include="..\error.cpp" />
<ClCompile Include="..\FileStream.cpp" />
<ClCompile Include="..\endian.cpp" />
<ClCompile Include="..\MemoryStream.cpp" />
<ClCompile Include="..\string\trim.cpp">
<Filter>string</Filter>
</ClCompile>
<ClCompile Include="..\general.cpp" />
<ClCompile Include="..\cdrom\audioreader.cpp">
<Filter>cdrom</Filter>
</ClCompile>
<ClCompile Include="..\Mednadisc.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\cdrom\CDAccess_Image.h">
<Filter>cdrom</Filter>
</ClInclude>
<ClInclude Include="..\cdrom\cdromif.h">
<Filter>cdrom</Filter>
</ClInclude>
<ClInclude Include="..\cdrom\CDUtility.h">
<Filter>cdrom</Filter>
</ClInclude>
<ClInclude Include="..\cdrom\dvdisaster.h">
<Filter>cdrom</Filter>
</ClInclude>
<ClInclude Include="..\cdrom\galois-inlines.h">
<Filter>cdrom</Filter>
</ClInclude>
<ClInclude Include="..\cdrom\lec.h">
<Filter>cdrom</Filter>
</ClInclude>
<ClInclude Include="..\cdrom\SimpleFIFO.h">
<Filter>cdrom</Filter>
</ClInclude>
<ClInclude Include="..\cdrom\CDAccess.h">
<Filter>cdrom</Filter>
</ClInclude>
<ClInclude Include="..\cdrom\CDAccess_CCD.h">
<Filter>cdrom</Filter>
</ClInclude>
<ClInclude Include="..\emuware\emuware.h">
<Filter>emuware</Filter>
</ClInclude>
<ClInclude Include="..\error.h" />
<ClInclude Include="..\Stream.h" />
<ClInclude Include="..\FileStream.h" />
<ClInclude Include="..\endian.h" />
<ClInclude Include="..\MemoryStream.h" />
<ClInclude Include="..\string\trim.h">
<Filter>string</Filter>
</ClInclude>
<ClInclude Include="..\general.h" />
<ClInclude Include="..\cdrom\audioreader.h">
<Filter>cdrom</Filter>
</ClInclude>
<ClInclude Include="..\Mednadisc.h" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,58 @@
/* Mednafen - Multi-system Emulator
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "emuware/emuware.h"
#include "CDAccess.h"
#include "CDAccess_Image.h"
#include "CDAccess_CCD.h"
#ifdef HAVE_LIBCDIO
#include "CDAccess_Physical.h"
#endif
using namespace CDUtility;
CDAccess::CDAccess()
{
}
CDAccess::~CDAccess()
{
}
CDAccess *cdaccess_open_image(const std::string& path, bool image_memcache)
{
CDAccess *ret = NULL;
if(path.size() >= 4 && !strcasecmp(path.c_str() + path.size() - 4, ".ccd"))
ret = new CDAccess_CCD(path, image_memcache);
else
ret = new CDAccess_Image(path, image_memcache);
return ret;
}
CDAccess *cdaccess_open_phys(const std::string& devicename)
{
#ifdef HAVE_LIBCDIO
return new CDAccess_Physical(devicename);
#else
throw MDFN_Error(0, ("Physical CD access support not compiled in."));
#endif
}

View File

@ -0,0 +1,32 @@
#ifndef __MDFN_CDROMFILE_H
#define __MDFN_CDROMFILE_H
#include <stdio.h>
#include <string>
#include "CDUtility.h"
class CDAccess
{
public:
CDAccess();
virtual ~CDAccess();
virtual void Read_Raw_Sector(uint8 *buf, int32 lba) = 0;
virtual void Read_TOC(CDUtility::TOC *toc) = 0;
virtual bool Is_Physical(void) throw() = 0;
virtual void Eject(bool eject_status) = 0; // Eject a disc if it's physical, otherwise NOP. Returns true on success(or NOP), false on error
private:
CDAccess(const CDAccess&); // No copy constructor.
CDAccess& operator=(const CDAccess&); // No assignment operator.
};
CDAccess *cdaccess_open_image(const std::string& path, bool image_memcache);
CDAccess *cdaccess_open_phys(const std::string& devicename);
#endif

View File

@ -0,0 +1,435 @@
/* Mednafen - Multi-system Emulator
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "emuware/emuware.h"
#include "../general.h"
#include "../string/trim.h"
#include "CDAccess_CCD.h"
//#include <trio/trio.h>
//wrapper to repair gettext stuff
#define _(X) X
#include <limits>
#include <limits.h>
#include <map>
using namespace CDUtility;
static void MDFN_strtoupper(std::string &str)
{
const size_t len = str.length();
for(size_t x = 0; x < len; x++)
{
if(str[x] >= 'a' && str[x] <= 'z')
{
str[x] = str[x] - 'a' + 'A';
}
}
}
typedef std::map<std::string, std::string> CCD_Section;
template<typename T>
static T CCD_ReadInt(CCD_Section &s, const std::string &propname, const bool have_defval = false, const int defval = 0)
{
CCD_Section::iterator zit = s.find(propname);
if(zit == s.end())
{
if(have_defval)
return defval;
else
throw MDFN_Error(0, _("Missing property: %s"), propname.c_str());
}
const std::string &v = zit->second;
int scan_base = 10;
size_t scan_offset = 0;
long ret = 0;
if(v.length() >= 3 && v[0] == '0' && v[1] == 'x')
{
scan_base = 16;
scan_offset = 2;
}
const char *vp = v.c_str() + scan_offset;
char *ep = NULL;
if(std::numeric_limits<T>::is_signed)
ret = strtol(vp, &ep, scan_base);
else
ret = strtoul(vp, &ep, scan_base);
if(!vp[0] || ep[0])
{
throw MDFN_Error(0, _("Property %s: Malformed integer: %s"), propname.c_str(), v.c_str());
}
//if(ret < minv || ret > maxv)
//{
// throw MDFN_Error(0, _("Property %s: Integer %ld out of range(accepted: %d through %d)."), propname.c_str(), ret, minv, maxv);
//}
return ret;
}
CDAccess_CCD::CDAccess_CCD(const std::string& path, bool image_memcache) : img_stream(NULL), sub_stream(NULL), img_numsectors(0)
{
try
{
Load(path, image_memcache);
}
catch(...)
{
Cleanup();
throw;
}
}
void CDAccess_CCD::Load(const std::string& path, bool image_memcache)
{
FileStream cf(path, FileStream::MODE_READ);
std::map<std::string, CCD_Section> Sections;
std::string linebuf;
std::string cur_section_name;
std::string dir_path, file_base, file_ext;
char img_extsd[4] = { 'i', 'm', 'g', 0 };
char sub_extsd[4] = { 's', 'u', 'b', 0 };
MDFN_GetFilePathComponents(path, &dir_path, &file_base, &file_ext);
if(file_ext.length() == 4 && file_ext[0] == '.')
{
signed char extupt[3] = { -1, -1, -1 };
for(int i = 1; i < 4; i++)
{
if(file_ext[i] >= 'A' && file_ext[i] <= 'Z')
extupt[i - 1] = 'A' - 'a';
else if(file_ext[i] >= 'a' && file_ext[i] <= 'z')
extupt[i - 1] = 0;
}
signed char av = -1;
for(int i = 0; i < 3; i++)
{
if(extupt[i] != -1)
av = extupt[i];
else
extupt[i] = av;
}
if(av == -1)
av = 0;
for(int i = 0; i < 3; i++)
{
if(extupt[i] == -1)
extupt[i] = av;
}
for(int i = 0; i < 3; i++)
{
img_extsd[i] += extupt[i];
sub_extsd[i] += extupt[i];
}
}
//printf("%s %d %d %d\n", file_ext.c_str(), extupt[0], extupt[1], extupt[2]);
linebuf.reserve(256);
while(cf.get_line(linebuf) >= 0)
{
MDFN_trim(linebuf);
if(linebuf.length() == 0) // Skip blank lines.
continue;
if(linebuf[0] == '[')
{
if(linebuf.length() < 3 || linebuf[linebuf.length() - 1] != ']')
throw MDFN_Error(0, _("Malformed section specifier: %s"), linebuf.c_str());
cur_section_name = linebuf.substr(1, linebuf.length() - 2);
MDFN_strtoupper(cur_section_name);
}
else
{
const size_t feqpos = linebuf.find('=');
const size_t leqpos = linebuf.rfind('=');
std::string k, v;
if(feqpos == std::string::npos || feqpos != leqpos)
throw MDFN_Error(0, _("Malformed value pair specifier: %s"), linebuf.c_str());
k = linebuf.substr(0, feqpos);
v = linebuf.substr(feqpos + 1);
MDFN_trim(k);
MDFN_trim(v);
MDFN_strtoupper(k);
Sections[cur_section_name][k] = v;
}
}
{
CCD_Section& ds = Sections["DISC"];
unsigned toc_entries = CCD_ReadInt<unsigned>(ds, "TOCENTRIES");
unsigned num_sessions = CCD_ReadInt<unsigned>(ds, "SESSIONS");
bool data_tracks_scrambled = CCD_ReadInt<unsigned>(ds, "DATATRACKSSCRAMBLED");
if(num_sessions != 1)
throw MDFN_Error(0, _("Unsupported number of sessions: %u"), num_sessions);
if(data_tracks_scrambled)
throw MDFN_Error(0, _("Scrambled CCD data tracks currently not supported."));
//printf("MOO: %d\n", toc_entries);
for(unsigned te = 0; te < toc_entries; te++)
{
char tmpbuf[64];
snprintf(tmpbuf, sizeof(tmpbuf), "ENTRY %u", te);
CCD_Section& ts = Sections[std::string(tmpbuf)];
unsigned session = CCD_ReadInt<unsigned>(ts, "SESSION");
uint8 point = CCD_ReadInt<uint8>(ts, "POINT");
uint8 adr = CCD_ReadInt<uint8>(ts, "ADR");
uint8 control = CCD_ReadInt<uint8>(ts, "CONTROL");
uint8 pmin = CCD_ReadInt<uint8>(ts, "PMIN");
uint8 psec = CCD_ReadInt<uint8>(ts, "PSEC");
//uint8 pframe = CCD_ReadInt<uint8>(ts, "PFRAME");
signed plba = CCD_ReadInt<signed>(ts, "PLBA");
if(session != 1)
throw MDFN_Error(0, "Unsupported TOC entry Session value: %u", session);
// Reference: ECMA-394, page 5-14
if(point >= 1 && point <= 99)
{
tocd.tracks[point].adr = adr;
tocd.tracks[point].control = control;
tocd.tracks[point].lba = plba;
}
else
switch(point)
{
default:
throw MDFN_Error(0, "Unsupported TOC entry Point value: %u", point);
break;
case 0xA0:
tocd.first_track = pmin;
tocd.disc_type = psec;
break;
case 0xA1:
tocd.last_track = pmin;
break;
case 0xA2:
tocd.tracks[100].adr = adr;
tocd.tracks[100].control = control;
tocd.tracks[100].lba = plba;
break;
break;
}
}
}
// Convenience leadout track duplication.
if(tocd.last_track < 99)
tocd.tracks[tocd.last_track + 1] = tocd.tracks[100];
//
// Open image stream.
{
std::string image_path = MDFN_EvalFIP(dir_path, file_base + std::string(".") + std::string(img_extsd), true);
if(image_memcache)
{
img_stream = new MemoryStream(new FileStream(image_path, FileStream::MODE_READ));
}
else
{
img_stream = new FileStream(image_path, FileStream::MODE_READ);
}
int64 ss = img_stream->size();
if(ss % 2352)
throw MDFN_Error(0, _("CCD image size is not evenly divisible by 2352."));
img_numsectors = ss / 2352;
}
//
// Open subchannel stream
{
std::string sub_path = MDFN_EvalFIP(dir_path, file_base + std::string(".") + std::string(sub_extsd), true);
if(image_memcache)
sub_stream = new MemoryStream(new FileStream(sub_path, FileStream::MODE_READ));
else
sub_stream = new FileStream(sub_path, FileStream::MODE_READ);
if(sub_stream->size() != (uint64)img_numsectors * 96)
throw MDFN_Error(0, _("CCD SUB file size mismatch."));
}
CheckSubQSanity();
}
//
// Checks for Q subchannel mode 1(current time) data that has a correct checksum, but the data is nonsensical or corrupted nonetheless; this is the
// case for some bad rips floating around on the Internet. Allowing these bad rips to be used will cause all sorts of problems during emulation, so we
// error out here if a bad rip is detected.
//
// This check is not as aggressive or exhaustive as it could be, and will not detect all potential Q subchannel rip errors; as such, it should definitely NOT be
// used in an effort to "repair" a broken rip.
//
void CDAccess_CCD::CheckSubQSanity(void)
{
size_t checksum_pass_counter = 0;
int prev_lba = INT_MAX;
uint8 prev_track = 0;
for(size_t s = 0; s < img_numsectors; s++)
{
union
{
uint8 full[96];
struct
{
uint8 pbuf[12];
uint8 qbuf[12];
};
} buf;
sub_stream->seek(s * 96, SEEK_SET);
sub_stream->read(buf.full, 96);
if(subq_check_checksum(buf.qbuf))
{
uint8 adr = buf.qbuf[0] & 0xF;
if(adr == 0x01)
{
uint8 track_bcd = buf.qbuf[1];
uint8 index_bcd = buf.qbuf[2];
uint8 rm_bcd = buf.qbuf[3];
uint8 rs_bcd = buf.qbuf[4];
uint8 rf_bcd = buf.qbuf[5];
uint8 am_bcd = buf.qbuf[7];
uint8 as_bcd = buf.qbuf[8];
uint8 af_bcd = buf.qbuf[9];
//printf("%2x %2x %2x\n", am_bcd, as_bcd, af_bcd);
if(!BCD_is_valid(track_bcd) || !BCD_is_valid(index_bcd) || !BCD_is_valid(rm_bcd) || !BCD_is_valid(rs_bcd) || !BCD_is_valid(rf_bcd) ||
!BCD_is_valid(am_bcd) || !BCD_is_valid(as_bcd) || !BCD_is_valid(af_bcd) ||
rs_bcd > 0x59 || rf_bcd > 0x74 || as_bcd > 0x59 || af_bcd > 0x74)
{
throw MDFN_Error(0, _("Garbage subchannel Q data detected(bad BCD/out of range): %02x:%02x:%02x %02x:%02x:%02x"), rm_bcd, rs_bcd, rf_bcd, am_bcd, as_bcd, af_bcd);
}
else
{
int lba = ((BCD_to_U8(am_bcd) * 60 + BCD_to_U8(as_bcd)) * 75 + BCD_to_U8(af_bcd)) - 150;
uint8 track = BCD_to_U8(track_bcd);
if(prev_lba != INT_MAX && abs(lba - prev_lba) > 100)
throw MDFN_Error(0, _("Garbage subchannel Q data detected(excessively large jump in AMSF)"));
if(abs(lba - (int)s) > 100)
throw MDFN_Error(0, _("Garbage subchannel Q data detected(AMSF value is out of tolerance)"));
prev_lba = lba;
if(track < prev_track)
throw MDFN_Error(0, _("Garbage subchannel Q data detected(bad track number)"));
//else if(prev_track && track - pre
prev_track = track;
}
checksum_pass_counter++;
}
}
}
//printf("%u/%u\n", checksum_pass_counter, img_numsectors);
}
void CDAccess_CCD::Cleanup(void)
{
if(img_stream)
{
delete img_stream;
img_stream = NULL;
}
if(sub_stream)
{
delete sub_stream;
sub_stream = NULL;
}
}
CDAccess_CCD::~CDAccess_CCD()
{
Cleanup();
}
void CDAccess_CCD::Read_Raw_Sector(uint8 *buf, int32 lba)
{
if(lba < 0 || (size_t)lba >= img_numsectors)
throw(MDFN_Error(0, _("LBA out of range.")));
uint8 sub_buf[96];
img_stream->seek(lba * 2352, SEEK_SET);
img_stream->read(buf, 2352);
sub_stream->seek(lba * 96, SEEK_SET);
sub_stream->read(sub_buf, 96);
subpw_interleave(sub_buf, buf + 2352);
}
void CDAccess_CCD::Read_TOC(CDUtility::TOC *toc)
{
*toc = tocd;
}
bool CDAccess_CCD::Is_Physical(void) throw()
{
return false;
}
void CDAccess_CCD::Eject(bool eject_status)
{
}

View File

@ -0,0 +1,50 @@
/* Mednafen - Multi-system Emulator
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "../FileStream.h"
#include "../MemoryStream.h"
#include "CDAccess.h"
#include <vector>
class CDAccess_CCD : public CDAccess
{
public:
CDAccess_CCD(const std::string& path, bool image_memcache);
virtual ~CDAccess_CCD();
virtual void Read_Raw_Sector(uint8 *buf, int32 lba);
virtual void Read_TOC(CDUtility::TOC *toc);
virtual bool Is_Physical(void) throw();
virtual void Eject(bool eject_status);
private:
void Load(const std::string& path, bool image_memcache);
void Cleanup(void);
void CheckSubQSanity(void);
Stream* img_stream;
Stream* sub_stream;
size_t img_numsectors;
CDUtility::TOC tocd;
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,102 @@
#ifndef __MDFN_CDACCESS_IMAGE_H
#define __MDFN_CDACCESS_IMAGE_H
#include <map>
#include <array>
class Stream;
class AudioReader;
struct CDRFILE_TRACK_INFO
{
int32 LBA;
uint32 DIFormat;
uint8 subq_control;
int32 pregap;
int32 pregap_dv;
int32 postgap;
int32 index[2];
int32 sectors; // Not including pregap sectors!
Stream *fp;
bool FirstFileInstance;
bool RawAudioMSBFirst;
long FileOffset;
unsigned int SubchannelMode;
uint32 LastSamplePos;
AudioReader *AReader;
};
#if 0
struct Medium_Chunk
{
int64 Offset; // Offset in [..TODO..]
uint32 DIFormat;
FILE *fp;
bool FirstFileInstance;
bool RawAudioMSBFirst;
unsigned int SubchannelMode;
uint32 LastSamplePos;
AudioReader *AReader;
};
struct CD_Chunk
{
int32 LBA;
int32 Track;
int32 Index;
bool DataType;
Medium_Chunk Medium;
};
static std::vector<CD_Chunk> Chunks;
#endif
class CDAccess_Image : public CDAccess
{
public:
CDAccess_Image(const std::string& path, bool image_memcache);
virtual ~CDAccess_Image();
virtual void Read_Raw_Sector(uint8 *buf, int32 lba);
virtual void Read_TOC(CDUtility::TOC *toc);
virtual bool Is_Physical(void) throw();
virtual void Eject(bool eject_status);
private:
int32 NumTracks;
int32 FirstTrack;
int32 LastTrack;
int32 total_sectors;
uint8 disc_type;
CDRFILE_TRACK_INFO Tracks[100]; // Track #0(HMM?) through 99
std::map<uint32, std::array<uint8, 12>> SubQReplaceMap;
std::string base_dir;
void ImageOpen(const std::string& path, bool image_memcache);
void LoadSBI(const std::string& sbi_path);
void Cleanup(void);
// MakeSubPQ will OR the simulated P and Q subchannel data into SubPWBuf.
void MakeSubPQ(int32 lba, uint8 *SubPWBuf);
void ParseTOCFileLineInfo(CDRFILE_TRACK_INFO *track, const int tracknum, const std::string &filename, const char *binoffset, const char *msfoffset, const char *length, bool image_memcache, std::map<std::string, Stream*> &toc_streamcache);
uint32 GetSectorCount(CDRFILE_TRACK_INFO *track);
};
#endif

View File

@ -0,0 +1,456 @@
/* Mednafen - Multi-system Emulator
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define EXTERNAL_LIBCDIO_CONFIG_H 1
#include "../mednafen.h"
#include "../general.h"
#include "CDAccess.h"
#include "CDAccess_Physical.h"
#include <time.h>
#include <stdlib.h>
#include <string>
#include <vector>
#include <cdio/cdio.h>
#include <cdio/mmc.h>
#include <cdio/logging.h>
#if LIBCDIO_VERSION_NUM >= 83
#include <cdio/mmc_cmds.h>
#endif
using namespace CDUtility;
static bool Logging = false;
static std::string LogMessage;
static void LogHandler(cdio_log_level_t level, const char message[])
{
if(!Logging)
return;
try
{
if(LogMessage.size() > 0)
LogMessage.append(" - ");
LogMessage.append(message);
}
catch(...) // Don't throw exceptions through libcdio's code.
{
LogMessage.clear();
}
}
static INLINE void StartLogging(void)
{
Logging = true;
LogMessage.clear();
}
static INLINE void ClearLogging(void)
{
LogMessage.clear();
}
static INLINE std::string StopLogging(void)
{
std::string ret = LogMessage;
Logging = false;
LogMessage.clear();
return(ret);
}
void CDAccess_Physical::DetermineFeatures(void)
{
uint8 buf[256];
mmc_cdb_t cdb = {{0, }};
CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_MODE_SENSE_10);
memset(buf, 0, sizeof(buf));
cdb.field[2] = 0x2A;
cdb.field[7] = sizeof(buf) >> 8;
cdb.field[8] = sizeof(buf) & 0xFF;
StartLogging();
if(mmc_run_cmd ((CdIo *)p_cdio, MMC_TIMEOUT_DEFAULT,
&cdb,
SCSI_MMC_DATA_READ,
sizeof(buf),
buf))
{
throw(MDFN_Error(0, _("MMC [MODE SENSE 10] command failed: %s"), StopLogging().c_str()));
}
else
{
const uint8 *pd = &buf[8];
StopLogging();
if(pd[0] != 0x2A || pd[1] < 0x14)
{
throw(MDFN_Error(0, _("MMC [MODE SENSE 10] command returned bogus data for mode page 0x2A.")));
}
if(!(pd[4] & 0x10))
{
throw(MDFN_Error(0, _("Drive does not support reading Mode 2 Form 1 sectors.")));
}
if(!(pd[4] & 0x20))
{
throw(MDFN_Error(0, _("Drive does not support reading Mode 2 Form 2 sectors.")));
}
if(!(pd[5] & 0x01))
{
throw(MDFN_Error(0, _("Reading CD-DA sectors via \"READ CD\" is not supported.")));
}
if(!(pd[5] & 0x02))
{
throw(MDFN_Error(0, _("Read CD-DA sectors via \"READ CD\" are not positionally-accurate.")));
}
if(!(pd[5] & 0x04))
{
throw(MDFN_Error(0, _("Reading raw subchannel data via \"READ CD\" is not supported.")));
}
}
}
void CDAccess_Physical::PreventAllowMediumRemoval(bool prevent)
{
#if 0
mmc_cdb_t cdb = {{0, }};
uint8 buf[8];
cdb.field[0] = 0x1E;
cdb.field[1] = 0x00;
cdb.field[2] = 0x00;
cdb.field[3] = 0x00;
cdb.field[4] = 0x00; //prevent;
cdb.field[5] = 0x00;
printf("%d\n", mmc_run_cmd_len (p_cdio, MMC_TIMEOUT_DEFAULT,
&cdb, 6,
SCSI_MMC_DATA_READ, 0, buf));
assert(0);
#endif
}
// To be used in the future for constructing semi-raw TOC data.
#if 0
static uint8 cond_hex_to_bcd(uint8 val)
{
if( ((val & 0xF) > 0x9) || ((val & 0xF0) > 0x90) )
return val;
return U8_to_BCD(val);
}
#endif
void CDAccess_Physical::ReadPhysDiscInfo(unsigned retry)
{
mmc_cdb_t cdb = {{0, }};
std::vector<uint8> toc_buffer;
int64 start_time = time(NULL);
int cdio_rc;
toc_buffer.resize(0x3FFF); // (2**(8 * 2 - 1 - 1)) - 1, in case the drive has buggy firmware which chops upper bits off or overflows with values near
// the max of a 16-bit signed value
cdb.field[0] = 0x43; // Read TOC
cdb.field[1] = 0x00;
cdb.field[2] = 0x02; // Format 0010b
cdb.field[3] = 0x00;
cdb.field[4] = 0x00;
cdb.field[5] = 0x00;
cdb.field[6] = 0x01; // First session number
cdb.field[7] = toc_buffer.size() >> 8;
cdb.field[8] = toc_buffer.size() & 0xFF;
cdb.field[9] = 0x00;
StartLogging();
while((cdio_rc = mmc_run_cmd ((CdIo *)p_cdio, MMC_TIMEOUT_DEFAULT,
&cdb,
SCSI_MMC_DATA_READ,
toc_buffer.size(),
&toc_buffer[0])))
{
if(!retry || time(NULL) >= (start_time + retry))
{
throw(MDFN_Error(0, _("Error reading disc TOC: %s"), StopLogging().c_str()));
}
else
ClearLogging();
}
StopLogging();
PhysTOC.Clear();
{
int32 len_counter = MDFN_de16msb(&toc_buffer[0]) - 2;
uint8 *tbi = &toc_buffer[4];
if(len_counter < 0 || (len_counter % 11) != 0)
throw MDFN_Error(0, _("READ TOC command response data is of an invalid length."));
while(len_counter)
{
// Ref: MMC-3 draft revision 10g, page 221
uint8 sess MDFN_NOWARN_UNUSED = tbi[0];
uint8 adr_ctrl = tbi[1];
uint8 tno MDFN_NOWARN_UNUSED = tbi[2];
uint8 point = tbi[3];
uint8 min MDFN_NOWARN_UNUSED = tbi[4];
uint8 sec MDFN_NOWARN_UNUSED = tbi[5];
uint8 frame MDFN_NOWARN_UNUSED = tbi[6];
uint8 hour_phour MDFN_NOWARN_UNUSED = tbi[7];
uint8 pmin = tbi[8];
uint8 psec = tbi[9];
uint8 pframe = tbi[10];
if((adr_ctrl >> 4) == 1)
{
switch(((adr_ctrl >> 4) << 8) | point)
{
case 0x101 ... 0x163:
PhysTOC.tracks[point].adr = adr_ctrl >> 4;
PhysTOC.tracks[point].control = adr_ctrl & 0xF;
PhysTOC.tracks[point].lba = AMSF_to_LBA(pmin, psec, pframe);
break;
case 0x1A0:
PhysTOC.first_track = pmin;
PhysTOC.disc_type = psec;
break;
case 0x1A1:
PhysTOC.last_track = pmin;
break;
case 0x1A2:
PhysTOC.tracks[100].adr = adr_ctrl >> 4;
PhysTOC.tracks[100].control = adr_ctrl & 0xF;
PhysTOC.tracks[100].lba = AMSF_to_LBA(pmin, psec, pframe);
break;
default:
//MDFN_printf("%02x %02x\n", adr_ctrl >> 4, point);
break;
}
}
tbi += 11;
len_counter -= 11;
}
}
if(PhysTOC.first_track < 1 || PhysTOC.first_track > 99)
{
throw(MDFN_Error(0, _("Invalid first track: %d\n"), PhysTOC.first_track));
}
if(PhysTOC.last_track > 99 || PhysTOC.last_track < PhysTOC.first_track)
{
throw(MDFN_Error(0, _("Invalid last track: %d\n"), PhysTOC.last_track));
}
// Convenience leadout track duplication.
if(PhysTOC.last_track < 99)
PhysTOC.tracks[PhysTOC.last_track + 1] = PhysTOC.tracks[100];
}
void CDAccess_Physical::Read_TOC(TOC *toc)
{
*toc = PhysTOC;
}
void CDAccess_Physical::Read_Raw_Sector(uint8 *buf, int32 lba)
{
mmc_cdb_t cdb = {{0, }};
int cdio_rc;
CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_READ_CD);
CDIO_MMC_SET_READ_TYPE (cdb.field, CDIO_MMC_READ_TYPE_ANY);
CDIO_MMC_SET_READ_LBA (cdb.field, lba);
CDIO_MMC_SET_READ_LENGTH24(cdb.field, 1);
StartLogging();
if(SkipSectorRead[(lba >> 3) & 0xFFFF] & (1 << (lba & 7)))
{
printf("Read(skipped): %d\n", lba);
memset(buf, 0, 2352);
cdb.field[9] = 0x00;
cdb.field[10] = 0x01;
if((cdio_rc = mmc_run_cmd ((CdIo *)p_cdio, MMC_TIMEOUT_DEFAULT,
&cdb,
SCSI_MMC_DATA_READ,
96,
buf + 2352)))
{
throw(MDFN_Error(0, _("MMC Read Error: %s"), StopLogging().c_str()));
}
}
else
{
cdb.field[9] = 0xF8;
cdb.field[10] = 0x01;
if((cdio_rc = mmc_run_cmd ((CdIo *)p_cdio, MMC_TIMEOUT_DEFAULT,
&cdb,
SCSI_MMC_DATA_READ,
2352 + 96,
buf)))
{
throw(MDFN_Error(0, _("MMC Read Error: %s"), StopLogging().c_str()));
}
}
StopLogging();
}
CDAccess_Physical::CDAccess_Physical(const std::string& path)
{
char **devices = NULL;
char **parseit = NULL;
p_cdio = NULL;
cdio_init();
cdio_log_set_handler(LogHandler);
//
//
//
try
{
devices = cdio_get_devices(DRIVER_DEVICE);
parseit = devices;
if(parseit)
{
MDFN_printf(_("Connected physical devices:\n"));
MDFN_indent(1);
while(*parseit)
{
MDFN_printf("%s\n", *parseit);
parseit++;
}
MDFN_indent(-1);
}
if(!parseit || parseit == devices)
{
throw(MDFN_Error(0, _("No CDROM drives detected(or no disc present).")));
}
if(devices)
{
cdio_free_device_list(devices);
devices = NULL;
}
StartLogging();
p_cdio = cdio_open_cd(path.c_str());
if(!p_cdio)
{
throw(MDFN_Error(0, _("Error opening physical CD: %s"), StopLogging().c_str()));
}
StopLogging();
//PreventAllowMediumRemoval(true);
ReadPhysDiscInfo(0);
//
// Determine how we can read this CD.
//
DetermineFeatures();
memset(SkipSectorRead, 0, sizeof(SkipSectorRead));
}
catch(std::exception &e)
{
if(devices)
cdio_free_device_list(devices);
if(p_cdio)
cdio_destroy((CdIo *)p_cdio);
throw;
}
}
CDAccess_Physical::~CDAccess_Physical()
{
cdio_destroy((CdIo *)p_cdio);
}
bool CDAccess_Physical::Is_Physical(void) throw()
{
return(true);
}
void CDAccess_Physical::Eject(bool eject_status)
{
int cdio_rc;
StartLogging();
#if LIBCDIO_VERSION_NUM >= 83
if((cdio_rc = mmc_start_stop_unit((CdIo *)p_cdio, eject_status, false, 0, 0)) != 0)
{
if(cdio_rc != DRIVER_OP_UNSUPPORTED) // Don't error out if it's just an unsupported operation.
throw(MDFN_Error(0, _("Error ejecting medium: %s"), StopLogging().c_str()));
}
#else
if((cdio_rc = mmc_start_stop_media((CdIo *)p_cdio, eject_status, false, 0)) != 0)
{
if(cdio_rc != DRIVER_OP_UNSUPPORTED) // Don't error out if it's just an unsupported operation.
throw(MDFN_Error(0, _("Error ejecting medium: %s"), StopLogging().c_str()));
}
#endif
StopLogging();
if(!eject_status)
{
try
{
ReadPhysDiscInfo(10);
}
catch(std::exception &e)
{
#if LIBCDIO_VERSION_NUM >= 83
mmc_start_stop_unit((CdIo *)p_cdio, true, false, 0, 0); // Eject disc, if possible.
#else
mmc_start_stop_media((CdIo *)p_cdio, true, false, 0); // Eject disc, if possible.
#endif
throw;
}
}
}

View File

@ -0,0 +1,39 @@
#ifndef __MDFN_CDACCESS_PHYSICAL_H
#define __MDFN_CDACCESS_PHYSICAL_H
//
// This class's methods are NOT re-entrant!
//
// Don't include <cdio.h> here, else it will pollute with its #define's.
class CDAccess_Physical : public CDAccess
{
public:
CDAccess_Physical(const std::string& path);
virtual ~CDAccess_Physical();
virtual void Read_Raw_Sector(uint8 *buf, int32 lba);
virtual void Read_TOC(CDUtility::TOC *toc);
virtual bool Is_Physical(void) throw();
virtual void Eject(bool eject_status);
private:
void *p_cdio;
void DetermineFeatures(void);
void ReadPhysDiscInfo(unsigned retry);
void PreventAllowMediumRemoval(bool prevent);
CDUtility::TOC PhysTOC;
// TODO: 1-bit per sector on the physical CD. If set, don't read that sector.
uint8 SkipSectorRead[65536];
};
#endif

View File

@ -0,0 +1,324 @@
/* Mednafen - Multi-system Emulator
*
* Subchannel Q CRC Code: Copyright (C) 1998 Andreas Mueller <mueller@daneb.ping.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <string.h>
#include <assert.h>
#include "emuware/emuware.h"
#include "CDUtility.h"
#include "dvdisaster.h"
#include "lec.h"
// Kill_LEC_Correct();
namespace CDUtility
{
// lookup table for crc calculation
static uint16 subq_crctab[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
};
static uint8 scramble_table[2352 - 12];
static bool CDUtility_Inited = false;
static void InitScrambleTable(void)
{
unsigned cv = 1;
for(unsigned i = 12; i < 2352; i++)
{
unsigned char z = 0;
for(int b = 0; b < 8; b++)
{
z |= (cv & 1) << b;
int feedback = ((cv >> 1) & 1) ^ (cv & 1);
cv = (cv >> 1) | (feedback << 14);
}
scramble_table[i - 12] = z;
}
//for(int i = 0; i < 2352 - 12; i++)
// printf("0x%02x, ", scramble_table[i]);
}
void CDUtility_Init(void)
{
if(!CDUtility_Inited)
{
Init_LEC_Correct();
InitScrambleTable();
CDUtility_Inited = true;
}
}
void encode_mode0_sector(uint32 aba, uint8 *sector_data)
{
CDUtility_Init();
lec_encode_mode0_sector(aba, sector_data);
}
void encode_mode1_sector(uint32 aba, uint8 *sector_data)
{
CDUtility_Init();
lec_encode_mode1_sector(aba, sector_data);
}
void encode_mode2_sector(uint32 aba, uint8 *sector_data)
{
CDUtility_Init();
lec_encode_mode2_sector(aba, sector_data);
}
void encode_mode2_form1_sector(uint32 aba, uint8 *sector_data)
{
CDUtility_Init();
lec_encode_mode2_form1_sector(aba, sector_data);
}
void encode_mode2_form2_sector(uint32 aba, uint8 *sector_data)
{
CDUtility_Init();
lec_encode_mode2_form2_sector(aba, sector_data);
}
bool edc_check(const uint8 *sector_data, bool xa)
{
CDUtility_Init();
return(CheckEDC(sector_data, xa));
}
bool edc_lec_check_and_correct(uint8 *sector_data, bool xa)
{
CDUtility_Init();
return(ValidateRawSector(sector_data, xa));
}
bool subq_check_checksum(const uint8 *SubQBuf)
{
uint16 crc = 0;
uint16 stored_crc = 0;
stored_crc = SubQBuf[0xA] << 8;
stored_crc |= SubQBuf[0xB];
for(int i = 0; i < 0xA; i++)
crc = subq_crctab[(crc >> 8) ^ SubQBuf[i]] ^ (crc << 8);
crc = ~crc;
return(crc == stored_crc);
}
void subq_generate_checksum(uint8 *buf)
{
uint16 crc = 0;
for(int i = 0; i < 0xA; i++)
crc = subq_crctab[(crc >> 8) ^ buf[i]] ^ (crc << 8);
// Checksum
buf[0xa] = ~(crc >> 8);
buf[0xb] = ~(crc);
}
void subq_deinterleave(const uint8 *SubPWBuf, uint8 *qbuf)
{
memset(qbuf, 0, 0xC);
for(int i = 0; i < 96; i++)
{
qbuf[i >> 3] |= ((SubPWBuf[i] >> 6) & 0x1) << (7 - (i & 0x7));
}
}
// Deinterleaves 96 bytes of subchannel P-W data from 96 bytes of interleaved subchannel PW data.
void subpw_deinterleave(const uint8 *in_buf, uint8 *out_buf)
{
assert(in_buf != out_buf);
memset(out_buf, 0, 96);
for(unsigned ch = 0; ch < 8; ch++)
{
for(unsigned i = 0; i < 96; i++)
{
out_buf[(ch * 12) + (i >> 3)] |= ((in_buf[i] >> (7 - ch)) & 0x1) << (7 - (i & 0x7));
}
}
}
// Interleaves 96 bytes of subchannel P-W data from 96 bytes of uninterleaved subchannel PW data.
void subpw_interleave(const uint8 *in_buf, uint8 *out_buf)
{
assert(in_buf != out_buf);
for(unsigned d = 0; d < 12; d++)
{
for(unsigned bitpoodle = 0; bitpoodle < 8; bitpoodle++)
{
uint8 rawb = 0;
for(unsigned ch = 0; ch < 8; ch++)
{
rawb |= ((in_buf[ch * 12 + d] >> (7 - bitpoodle)) & 1) << (7 - ch);
}
out_buf[(d << 3) + bitpoodle] = rawb;
}
}
}
// NOTES ON LEADOUT AREA SYNTHESIS
//
// I'm not trusting that the "control" field for the TOC leadout entry will always be set properly, so | the control fields for the last track entry
// and the leadout entry together before extracting the D2 bit. Audio track->data leadout is fairly benign though maybe noisy(especially if we ever implement
// data scrambling properly), but data track->audio leadout could break things in an insidious manner for the more accurate drive emulation code).
//
void subpw_synth_leadout_lba(const TOC& toc, const int32 lba, uint8* SubPWBuf)
{
uint8 buf[0xC];
uint32 lba_relative;
uint32 ma, sa, fa;
uint32 m, s, f;
lba_relative = lba - toc.tracks[100].lba;
f = (lba_relative % 75);
s = ((lba_relative / 75) % 60);
m = (lba_relative / 75 / 60);
fa = (lba + 150) % 75;
sa = ((lba + 150) / 75) % 60;
ma = ((lba + 150) / 75 / 60);
uint8 adr = 0x1; // Q channel data encodes position
uint8 control = (toc.tracks[toc.last_track].control & 0x4) | toc.tracks[100].control;
memset(buf, 0, 0xC);
buf[0] = (adr << 0) | (control << 4);
buf[1] = 0xAA;
buf[2] = 0x01;
// Track relative MSF address
buf[3] = U8_to_BCD(m);
buf[4] = U8_to_BCD(s);
buf[5] = U8_to_BCD(f);
buf[6] = 0; // Zerroooo
// Absolute MSF address
buf[7] = U8_to_BCD(ma);
buf[8] = U8_to_BCD(sa);
buf[9] = U8_to_BCD(fa);
subq_generate_checksum(buf);
for(int i = 0; i < 96; i++)
SubPWBuf[i] = (((buf[i >> 3] >> (7 - (i & 0x7))) & 1) ? 0x40 : 0x00) | 0x80;
}
void synth_leadout_sector_lba(const uint8 mode, const TOC& toc, const int32 lba, uint8* out_buf)
{
memset(out_buf, 0, 2352 + 96);
subpw_synth_leadout_lba(toc, lba, out_buf + 2352);
if((toc.tracks[toc.last_track].control | toc.tracks[100].control) & 0x4)
{
switch(mode)
{
default:
encode_mode0_sector(LBA_to_ABA(lba), out_buf);
break;
case 0x01:
encode_mode1_sector(LBA_to_ABA(lba), out_buf);
break;
case 0x02:
out_buf[18] = 0x20;
encode_mode2_form2_sector(LBA_to_ABA(lba), out_buf);
break;
}
}
}
#if 0
bool subq_extrapolate(const uint8 *subq_input, int32 position_delta, uint8 *subq_output)
{
assert(subq_check_checksum(subq_input));
subq_generate_checksum(subq_output);
}
#endif
void scrambleize_data_sector(uint8 *sector_data)
{
for(unsigned i = 12; i < 2352; i++)
sector_data[i] ^= scramble_table[i - 12];
}
}

View File

@ -0,0 +1,225 @@
#ifndef __MDFN_CDROM_CDUTILITY_H
#define __MDFN_CDROM_CDUTILITY_H
namespace CDUtility
{
// Call once at app startup before creating any threads that could potentially cause re-entrancy to these functions.
// It will also be called automatically if needed for the first time a function in this namespace that requires
// the initialization function to be called is called, for potential
// usage in constructors of statically-declared objects.
void CDUtility_Init(void);
// Quick definitions here:
//
// ABA - Absolute block address, synonymous to absolute MSF
// aba = (m_a * 60 * 75) + (s_a * 75) + f_a
//
// LBA - Logical block address(related: data CDs are required to have a pregap of 2 seconds, IE 150 frames/sectors)
// lba = aba - 150
enum
{
ADR_NOQINFO = 0x00,
ADR_CURPOS = 0x01,
ADR_MCN = 0x02,
ADR_ISRC = 0x03
};
struct TOC_Track
{
uint8 adr;
uint8 control;
uint32 lba;
};
// SubQ control field flags.
enum
{
SUBQ_CTRLF_PRE = 0x01, // With 50/15us pre-emphasis.
SUBQ_CTRLF_DCP = 0x02, // Digital copy permitted.
SUBQ_CTRLF_DATA = 0x04, // Data track.
SUBQ_CTRLF_4CH = 0x08, // 4-channel CD-DA.
};
enum
{
DISC_TYPE_CDDA_OR_M1 = 0x00,
DISC_TYPE_CD_I = 0x10,
DISC_TYPE_CD_XA = 0x20
};
struct TOC
{
INLINE TOC()
{
Clear();
}
INLINE void Clear(void)
{
first_track = last_track = 0;
disc_type = 0;
memset(tracks, 0, sizeof(tracks)); // FIXME if we change TOC_Track to non-POD type.
}
INLINE int FindTrackByLBA(uint32 LBA)
{
for(int32 track = first_track; track <= (last_track + 1); track++)
{
if(track == (last_track + 1))
{
if(LBA < tracks[100].lba)
return(track - 1);
}
else
{
if(LBA < tracks[track].lba)
return(track - 1);
}
}
return(0);
}
uint8 first_track;
uint8 last_track;
uint8 disc_type;
TOC_Track tracks[100 + 1]; // [0] is unused, [100] is for the leadout track.
// Also, for convenience, tracks[last_track + 1] will always refer
// to the leadout track(even if last_track < 99, IE the leadout track details are duplicated).
};
//
// Address conversion functions.
//
static INLINE uint32 AMSF_to_ABA(int32 m_a, int32 s_a, int32 f_a)
{
return(f_a + 75 * s_a + 75 * 60 * m_a);
}
static INLINE void ABA_to_AMSF(uint32 aba, uint8 *m_a, uint8 *s_a, uint8 *f_a)
{
*m_a = aba / 75 / 60;
*s_a = (aba - *m_a * 75 * 60) / 75;
*f_a = aba - (*m_a * 75 * 60) - (*s_a * 75);
}
static INLINE int32 ABA_to_LBA(uint32 aba)
{
return(aba - 150);
}
static INLINE uint32 LBA_to_ABA(int32 lba)
{
return(lba + 150);
}
static INLINE int32 AMSF_to_LBA(uint8 m_a, uint8 s_a, uint8 f_a)
{
return(ABA_to_LBA(AMSF_to_ABA(m_a, s_a, f_a)));
}
static INLINE void LBA_to_AMSF(int32 lba, uint8 *m_a, uint8 *s_a, uint8 *f_a)
{
ABA_to_AMSF(LBA_to_ABA(lba), m_a, s_a, f_a);
}
//
// BCD conversion functions
//
static INLINE bool BCD_is_valid(uint8 bcd_number)
{
if((bcd_number & 0xF0) >= 0xA0)
return(false);
if((bcd_number & 0x0F) >= 0x0A)
return(false);
return(true);
}
static INLINE uint8 BCD_to_U8(uint8 bcd_number)
{
return( ((bcd_number >> 4) * 10) + (bcd_number & 0x0F) );
}
static INLINE uint8 U8_to_BCD(uint8 num)
{
return( ((num / 10) << 4) + (num % 10) );
}
// should always perform the conversion, even if the bcd number is invalid.
static INLINE bool BCD_to_U8_check(uint8 bcd_number, uint8 *out_number)
{
*out_number = BCD_to_U8(bcd_number);
if(!BCD_is_valid(bcd_number))
return(false);
return(true);
}
//
// Sector data encoding functions(to full 2352 bytes raw sector).
//
// sector_data must be able to contain at least 2352 bytes.
void encode_mode0_sector(uint32 aba, uint8 *sector_data);
void encode_mode1_sector(uint32 aba, uint8 *sector_data); // 2048 bytes of user data at offset 16
void encode_mode2_sector(uint32 aba, uint8 *sector_data); // 2336 bytes of user data at offset 16
void encode_mode2_form1_sector(uint32 aba, uint8 *sector_data); // 2048+8 bytes of user data at offset 16
void encode_mode2_form2_sector(uint32 aba, uint8 *sector_data); // 2324+8 bytes of user data at offset 16
// out_buf must be able to contain 2352+96 bytes.
// "mode" is only used if(toc.tracks[100].control & 0x4)
void synth_leadout_sector_lba(const uint8 mode, const TOC& toc, const int32 lba, uint8* out_buf);
//
// User data error detection and correction
//
// Check EDC of a mode 1 or mode 2 form 1 sector.
// Returns "true" if checksum is ok(matches).
// Returns "false" if checksum mismatch.
// sector_data should contain 2352 bytes of raw sector data.
bool edc_check(const uint8 *sector_data, bool xa);
// Check EDC and L-EC data of a mode 1 or mode 2 form 1 sector, and correct bit errors if any exist.
// Returns "true" if errors weren't detected, or they were corrected succesfully.
// Returns "false" if errors couldn't be corrected.
// sector_data should contain 2352 bytes of raw sector data.
bool edc_lec_check_and_correct(uint8 *sector_data, bool xa);
//
// Subchannel(Q in particular) functions
//
// Returns false on checksum mismatch, true on match.
bool subq_check_checksum(const uint8 *subq_buf);
// Calculates the checksum of Q subchannel data(not including the checksum bytes of course ;)) from subq_buf, and stores it into the appropriate position
// in subq_buf.
void subq_generate_checksum(uint8 *subq_buf);
// Deinterleaves 12 bytes of subchannel Q data from 96 bytes of interleaved subchannel PW data.
void subq_deinterleave(const uint8 *subpw_buf, uint8 *subq_buf);
// Deinterleaves 96 bytes of subchannel P-W data from 96 bytes of interleaved subchannel PW data.
void subpw_deinterleave(const uint8 *in_buf, uint8 *out_buf);
// Interleaves 96 bytes of subchannel P-W data from 96 bytes of uninterleaved subchannel PW data.
void subpw_interleave(const uint8 *in_buf, uint8 *out_buf);
// Extrapolates Q subchannel current position data from subq_input, with frame/sector delta position_delta, and writes to subq_output.
// Only valid for ADR_CURPOS.
// subq_input must pass subq_check_checksum().
// TODO
//void subq_extrapolate(const uint8 *subq_input, int32 position_delta, uint8 *subq_output);
// (De)Scrambles data sector.
void scrambleize_data_sector(uint8 *sector_data);
}
#endif

View File

@ -0,0 +1,7 @@
mednafen_SOURCES += cdrom/audioreader.cpp cdrom/cdromif.cpp cdrom/scsicd.cpp
mednafen_SOURCES += cdrom/CDUtility.cpp cdrom/crc32.cpp cdrom/galois.cpp cdrom/l-ec.cpp cdrom/recover-raw.cpp
mednafen_SOURCES += cdrom/lec.cpp cdrom/CDAccess.cpp cdrom/CDAccess_Image.cpp cdrom/CDAccess_CCD.cpp
if HAVE_LIBCDIO
mednafen_SOURCES += cdrom/CDAccess_Physical.cpp
endif

View File

@ -0,0 +1,140 @@
#ifndef __MDFN_SIMPLEFIFO_H
#define __MDFN_SIMPLEFIFO_H
#include <vector>
#include <assert.h>
#include "../math_ops.h"
template<typename T>
class SimpleFIFO
{
public:
// Constructor
SimpleFIFO(uint32 the_size) // Size should be a power of 2!
{
data.resize(round_up_pow2(the_size));
size = the_size;
read_pos = 0;
write_pos = 0;
in_count = 0;
}
// Destructor
INLINE ~SimpleFIFO()
{
}
INLINE void SaveStatePostLoad(void)
{
read_pos %= data.size();
write_pos %= data.size();
in_count %= (data.size() + 1);
}
#if 0
INLINE int StateAction(StateMem *sm, int load, int data_only, const char* sname)
{
SFORMAT StateRegs[] =
{
std::vector<T> data;
uint32 size;
SFVAR(read_pos),
SFVAR(write_pos),
SFVAR(in_count),
SFEND;
}
int ret = MDFNSS_StateAction(sm, load, data_only, sname);
if(load)
{
read_pos %= data.size();
write_pos %= data.size();
in_count %= (data.size() + 1);
}
return(ret);
}
#endif
INLINE uint32 CanRead(void)
{
return(in_count);
}
INLINE uint32 CanWrite(void)
{
return(size - in_count);
}
INLINE T ReadUnit(bool peek = false)
{
T ret;
assert(in_count > 0);
ret = data[read_pos];
if(!peek)
{
read_pos = (read_pos + 1) & (data.size() - 1);
in_count--;
}
return(ret);
}
INLINE uint8 ReadByte(bool peek = false)
{
assert(sizeof(T) == 1);
return(ReadUnit(peek));
}
INLINE void Write(const T *happy_data, uint32 happy_count)
{
assert(CanWrite() >= happy_count);
while(happy_count)
{
data[write_pos] = *happy_data;
write_pos = (write_pos + 1) & (data.size() - 1);
in_count++;
happy_data++;
happy_count--;
}
}
INLINE void WriteUnit(const T& wr_data)
{
Write(&wr_data, 1);
}
INLINE void WriteByte(const T& wr_data)
{
assert(sizeof(T) == 1);
Write(&wr_data, 1);
}
INLINE void Flush(void)
{
read_pos = 0;
write_pos = 0;
in_count = 0;
}
//private:
std::vector<T> data;
uint32 size;
uint32 read_pos; // Read position
uint32 write_pos; // Write position
uint32 in_count; // Number of units in the FIFO
};
#endif

View File

@ -0,0 +1,617 @@
/* Mednafen - Multi-system Emulator
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
// AR_Open(), and AudioReader, will NOT take "ownership" of the Stream object(IE it won't ever delete it). Though it does assume it has exclusive access
// to it for as long as the AudioReader object exists.
// Don't allow exceptions to propagate into the vorbis/musepack/etc. libraries, as it could easily leave the state of the library's decoder "object" in an
// inconsistent state, which would cause all sorts of unfun when we try to destroy it while handling the exception farther up.
#include "emuware/emuware.h"
#include "audioreader.h"
#include <sys/types.h>
#include <sys/stat.h>
#ifdef HAVE_AUDIOREADER
#include "../tremor/ivorbisfile.h"
#include "../mpcdec/mpcdec.h"
#endif
#ifdef HAVE_LIBSNDFILE
#include <sndfile.h>
#endif
#ifdef HAVE_OPUSFILE
#include "audioreader_opus.h"
#endif
#include <string.h>
#include <errno.h>
#include <time.h>
#include "../general.h"
#include "../endian.h"
AudioReader::AudioReader() : LastReadPos(0)
{
}
AudioReader::~AudioReader()
{
}
int64 AudioReader::Read_(int16 *buffer, int64 frames)
{
abort();
return(false);
}
bool AudioReader::Seek_(int64 frame_offset)
{
abort();
return(false);
}
int64 AudioReader::FrameCount(void)
{
abort();
return(0);
}
/*
**
**
**
**
**
**
**
**
**
*/
#ifdef HAVE_AUDIOREADER
class OggVorbisReader : public AudioReader
{
public:
OggVorbisReader(Stream *fp);
~OggVorbisReader();
int64 Read_(int16 *buffer, int64 frames);
bool Seek_(int64 frame_offset);
int64 FrameCount(void);
private:
OggVorbis_File ovfile;
Stream *fw;
};
static size_t iov_read_func(void *ptr, size_t size, size_t nmemb, void *user_data)
{
Stream *fw = (Stream*)user_data;
if(!size)
return(0);
try
{
return fw->read(ptr, size * nmemb, false) / size;
}
catch(...)
{
return(0);
}
}
static int iov_seek_func(void *user_data, ogg_int64_t offset, int whence)
{
Stream *fw = (Stream*)user_data;
try
{
fw->seek(offset, whence);
return(0);
}
catch(...)
{
return(-1);
}
}
static int iov_close_func(void *user_data)
{
Stream *fw = (Stream*)user_data;
try
{
fw->close();
return(0);
}
catch(...)
{
return EOF;
}
}
static long iov_tell_func(void *user_data)
{
Stream *fw = (Stream*)user_data;
try
{
return fw->tell();
}
catch(...)
{
return(-1);
}
}
OggVorbisReader::OggVorbisReader(Stream *fp) : fw(fp)
{
ov_callbacks cb;
memset(&cb, 0, sizeof(cb));
cb.read_func = iov_read_func;
cb.seek_func = iov_seek_func;
cb.close_func = iov_close_func;
cb.tell_func = iov_tell_func;
fp->seek(0, SEEK_SET);
if(ov_open_callbacks(fp, &ovfile, NULL, 0, cb))
throw(0);
}
OggVorbisReader::~OggVorbisReader()
{
ov_clear(&ovfile);
}
int64 OggVorbisReader::Read_(int16 *buffer, int64 frames)
{
uint8 *tw_buf = (uint8 *)buffer;
int cursection = 0;
long toread = frames * sizeof(int16) * 2;
while(toread > 0)
{
long didread = ov_read(&ovfile, (char*)tw_buf, toread, &cursection);
if(didread == 0)
break;
tw_buf = (uint8 *)tw_buf + didread;
toread -= didread;
}
return(frames - toread / sizeof(int16) / 2);
}
bool OggVorbisReader::Seek_(int64 frame_offset)
{
ov_pcm_seek(&ovfile, frame_offset);
return(true);
}
int64 OggVorbisReader::FrameCount(void)
{
return(ov_pcm_total(&ovfile, -1));
}
class MPCReader : public AudioReader
{
public:
MPCReader(Stream *fp);
~MPCReader();
int64 Read_(int16 *buffer, int64 frames);
bool Seek_(int64 frame_offset);
int64 FrameCount(void);
private:
mpc_reader reader;
mpc_demux *demux;
mpc_streaminfo si;
MPC_SAMPLE_FORMAT MPCBuffer[MPC_DECODER_BUFFER_LENGTH];
uint32 MPCBufferIn;
uint32 MPCBufferOffs;
Stream *fw;
};
/// Reads size bytes of data into buffer at ptr.
static mpc_int32_t impc_read(mpc_reader *p_reader, void *ptr, mpc_int32_t size)
{
Stream *fw = (Stream*)(p_reader->data);
try
{
return fw->read(ptr, size, false);
}
catch(...)
{
return(MPC_STATUS_FAIL);
}
}
/// Seeks to byte position offset.
static mpc_bool_t impc_seek(mpc_reader *p_reader, mpc_int32_t offset)
{
Stream *fw = (Stream*)(p_reader->data);
try
{
fw->seek(offset, SEEK_SET);
return(MPC_TRUE);
}
catch(...)
{
return(MPC_FALSE);
}
}
/// Returns the current byte offset in the stream.
static mpc_int32_t impc_tell(mpc_reader *p_reader)
{
Stream *fw = (Stream*)(p_reader->data);
try
{
return fw->tell();
}
catch(...)
{
return(MPC_STATUS_FAIL);
}
}
/// Returns the total length of the source stream, in bytes.
static mpc_int32_t impc_get_size(mpc_reader *p_reader)
{
Stream *fw = (Stream*)(p_reader->data);
try
{
return fw->size();
}
catch(...)
{
return(MPC_STATUS_FAIL);
}
}
/// True if the stream is a seekable stream.
static mpc_bool_t impc_canseek(mpc_reader *p_reader)
{
return(MPC_TRUE);
}
MPCReader::MPCReader(Stream *fp) : fw(fp)
{
fp->seek(0, SEEK_SET);
demux = NULL;
memset(&si, 0, sizeof(si));
memset(MPCBuffer, 0, sizeof(MPCBuffer));
MPCBufferOffs = 0;
MPCBufferIn = 0;
memset(&reader, 0, sizeof(reader));
reader.read = impc_read;
reader.seek = impc_seek;
reader.tell = impc_tell;
reader.get_size = impc_get_size;
reader.canseek = impc_canseek;
reader.data = (void*)fp;
if(!(demux = mpc_demux_init(&reader)))
{
throw(0);
}
mpc_demux_get_info(demux, &si);
if(si.channels != 2)
{
mpc_demux_exit(demux);
demux = NULL;
throw MDFN_Error(0, _("MusePack stream has wrong number of channels(%u); the correct number is 2."), si.channels);
}
if(si.sample_freq != 44100)
{
mpc_demux_exit(demux);
demux = NULL;
throw MDFN_Error(0, _("MusePack stream has wrong samplerate(%u Hz); the correct samplerate is 44100 Hz."), si.sample_freq);
}
}
MPCReader::~MPCReader()
{
if(demux)
{
mpc_demux_exit(demux);
demux = NULL;
}
}
int64 MPCReader::Read_(int16 *buffer, int64 frames)
{
mpc_status err;
int16 *cowbuf = (int16 *)buffer;
int32 toread = frames * 2;
while(toread > 0)
{
int32 tmplen;
if(!MPCBufferIn)
{
mpc_frame_info fi;
memset(&fi, 0, sizeof(fi));
fi.buffer = MPCBuffer;
if((err = mpc_demux_decode(demux, &fi)) < 0 || fi.bits == -1)
return(frames - toread / 2);
MPCBufferIn = fi.samples * 2;
MPCBufferOffs = 0;
}
tmplen = MPCBufferIn;
if(tmplen >= toread)
tmplen = toread;
for(int x = 0; x < tmplen; x++)
{
#ifdef MPC_FIXED_POINT
int32 samp = MPCBuffer[MPCBufferOffs + x] >> MPC_FIXED_POINT_FRACTPART;
#else
#warning Floating-point MPC decoding path not tested.
int32 samp = (int32)(MPCBuffer[MPCBufferOffs + x] * 32767);
#endif
if(samp < -32768)
samp = -32768;
if(samp > 32767)
samp = 32767;
*cowbuf = (int16)samp;
cowbuf++;
}
MPCBufferOffs += tmplen;
toread -= tmplen;
MPCBufferIn -= tmplen;
}
return(frames - toread / 2);
}
bool MPCReader::Seek_(int64 frame_offset)
{
MPCBufferOffs = 0;
MPCBufferIn = 0;
if(mpc_demux_seek_sample(demux, frame_offset) < 0)
return(false);
return(true);
}
int64 MPCReader::FrameCount(void)
{
return(mpc_streaminfo_get_length_samples(&si));
}
#endif //HAVE_AUDIOREADER
/*
**
**
**
**
**
**
**
**
**
*/
#ifdef HAVE_LIBSNDFILE
class SFReader : public AudioReader
{
public:
SFReader(Stream *fp);
~SFReader();
int64 Read_(int16 *buffer, int64 frames);
bool Seek_(int64 frame_offset);
int64 FrameCount(void);
private:
SNDFILE *sf;
SF_INFO sfinfo;
SF_VIRTUAL_IO sfvf;
Stream *fw;
};
static sf_count_t isf_get_filelen(void *user_data)
{
Stream *fw = (Stream*)user_data;
try
{
return fw->size();
}
catch(...)
{
return(-1);
}
}
static sf_count_t isf_seek(sf_count_t offset, int whence, void *user_data)
{
Stream *fw = (Stream*)user_data;
try
{
//printf("Seek: offset=%lld, whence=%lld\n", (long long)offset, (long long)whence);
fw->seek(offset, whence);
return fw->tell();
}
catch(...)
{
//printf(" SEEK FAILED\n");
return(-1);
}
}
static sf_count_t isf_read(void *ptr, sf_count_t count, void *user_data)
{
Stream *fw = (Stream*)user_data;
try
{
sf_count_t ret = fw->read(ptr, count, false);
//printf("Read: count=%lld, ret=%lld\n", (long long)count, (long long)ret);
return ret;
}
catch(...)
{
//printf(" READ FAILED\n");
return(0);
}
}
static sf_count_t isf_write(const void *ptr, sf_count_t count, void *user_data)
{
return(0);
}
static sf_count_t isf_tell(void *user_data)
{
Stream *fw = (Stream*)user_data;
try
{
return fw->tell();
}
catch(...)
{
return(-1);
}
}
SFReader::SFReader(Stream *fp) : fw(fp)
{
fp->seek(0, SEEK_SET);
memset(&sfvf, 0, sizeof(sfvf));
sfvf.get_filelen = isf_get_filelen;
sfvf.seek = isf_seek;
sfvf.read = isf_read;
sfvf.write = isf_write;
sfvf.tell = isf_tell;
memset(&sfinfo, 0, sizeof(sfinfo));
if(!(sf = sf_open_virtual(&sfvf, SFM_READ, &sfinfo, (void*)fp)))
throw(0);
}
SFReader::~SFReader()
{
sf_close(sf);
}
int64 SFReader::Read_(int16 *buffer, int64 frames)
{
return(sf_read_short(sf, (short*)buffer, frames * 2) / 2);
}
bool SFReader::Seek_(int64 frame_offset)
{
// FIXME error condition
if(sf_seek(sf, frame_offset, SEEK_SET) != frame_offset)
return(false);
return(true);
}
int64 SFReader::FrameCount(void)
{
return(sfinfo.frames);
}
#endif
AudioReader *AR_Open(Stream *fp)
{
#ifdef HAVE_AUDIOREADER
try
{
return new MPCReader(fp);
}
catch(int i)
{
}
#endif
#ifdef HAVE_OPUSFILE
try
{
return new OpusReader(fp);
}
catch(int i)
{
}
#endif
#ifdef HAVE_AUDIOREADER
try
{
return new OggVorbisReader(fp);
}
catch(int i)
{
}
#endif
#ifdef HAVE_LIBSNDFILE
try
{
return new SFReader(fp);
}
catch(int i)
{
}
#endif
return(NULL);
}

View File

@ -0,0 +1,43 @@
#ifndef __MDFN_AUDIOREADER_H
#define __MDFN_AUDIOREADER_H
#include "../Stream.h"
class AudioReader
{
public:
AudioReader();
virtual ~AudioReader();
virtual int64 FrameCount(void);
INLINE int64 Read(int64 frame_offset, int16 *buffer, int64 frames)
{
int64 ret;
//if(frame_offset >= 0)
{
if(LastReadPos != frame_offset)
{
//puts("SEEK");
if(!Seek_(frame_offset))
return(0);
LastReadPos = frame_offset;
}
}
ret = Read_(buffer, frames);
LastReadPos += ret;
return(ret);
}
private:
virtual int64 Read_(int16 *buffer, int64 frames);
virtual bool Seek_(int64 frame_offset);
int64 LastReadPos;
};
// AR_Open(), and AudioReader, will NOT take "ownership" of the Stream object(IE it won't ever delete it). Though it does assume it has exclusive access
// to it for as long as the AudioReader object exists.
AudioReader *AR_Open(Stream *fp);
#endif

View File

@ -0,0 +1,185 @@
/* Mednafen - Multi-system Emulator
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "../mednafen.h"
#include "audioreader.h"
#include "audioreader_opus.h"
// OPUS SUPPORT NOT DONE YET!!!
/*
(int64)op_pcm_total() * 44100 / 48000
resampling vs seek, filter delay, etc. to consider
*/
static size_t iop_read_func(void *ptr, size_t size, size_t nmemb, void *user_data)
{
Stream *fw = (Stream*)user_data;
if(!size)
return(0);
try
{
return fw->read(ptr, size * nmemb, false) / size;
}
catch(...)
{
return(0);
}
}
static int iop_seek_func(void *user_data, opus_int64 offset, int whence)
{
Stream *fw = (Stream*)user_data;
try
{
fw->seek(offset, whence);
return(0);
}
catch(...)
{
return(-1);
}
}
static int iop_close_func(void *user_data)
{
Stream *fw = (Stream*)user_data;
try
{
fw->close();
return(0);
}
catch(...)
{
return EOF;
}
}
static opus_int64 iop_tell_func(void *user_data)
{
Stream *fw = (Stream*)user_data;
try
{
return fw->tell();
}
catch(...)
{
return(-1);
}
}
/* Error strings copied from libopusfile header file comments. */
static const char *op_errstring(int error)
{
static const struct
{
int code;
const char *str;
} error_table[] =
{
{ OP_EREAD, gettext_noop("OP_EREAD: An underlying read, seek, or tell operation failed when it should have succeeded.") },
{ OP_EFAULT, gettext_noop("OP_EFAULT: A NULL pointer was passed where one was unexpected, or an internal memory allocation failed, or an internal library error was encountered.") },
{ OP_EIMPL, gettext_noop("OP_EIMPL: The stream used a feature that is not implemented, such as an unsupported channel family.") },
{ OP_EINVAL, gettext_noop("OP_EINVAL: One or more parameters to a function were invalid.") },
{ OP_ENOTFORMAT, gettext_noop("OP_ENOTFORMAT: A purported Ogg Opus stream did not begin with an Ogg page, or a purported header packet did not start with one of the required strings, \"OpusHead\" or \"OpusTags\".") },
{ OP_EBADHEADER, gettext_noop("OP_EBADHEADER: A required header packet was not properly formatted, contained illegal values, or was missing altogether.") },
{ OP_EVERSION, gettext_noop("OP_EVERSION: The ID header contained an unrecognized version number.") },
{ OP_EBADPACKET, gettext_noop("OP_EBADPACKET: An audio packet failed to decode properly.") },
{ OP_EBADLINK, gettext_noop("OP_EBADLINK: We failed to find data we had seen before, or the bitstream structure was sufficiently malformed that seeking to the target destination was impossible.") },
{ OP_ENOSEEK, gettext_noop("OP_ENOSEEK: An operation that requires seeking was requested on an unseekable stream.") },
{ OP_EBADTIMESTAMP, gettext_noop("OP_EBADTIMESTAMP: The first or last granule position of a link failed basic validity checks.") },
};
for(unsigned i = 0; i < sizeof(error_table) / sizeof(error_table[0]); i++)
{
if(error_table[i].code == error)
{
return _(error_table[i].str);
}
}
return _("Unknown");
}
OggOpusReader::OggOpusReader(Stream *fp) : fw(fp)
{
OpusFileCallbacks cb;
int error = 0;
memset(&cb, 0, sizeof(cb));
cb.read_func = iop_read_func;
cb.seek_func = iop_seek_func;
cb.close_func = iop_close_func;
cb.tell_func = iop_tell_func;
fp->seek(0, SEEK_SET);
if(!(opfile = op_open_callbacks((void*)fp, &cb, NULL, 0, &error)))
{
switch(error)
{
default:
throw MDFN_Error(0, _("opusfile: error code: %d(%s)", error, op_errstring(error)));
break;
case OP_ENOTFORMAT:
throw(0);
break;
}
}
}
OggOpusReader::~OggOpusReader()
{
op_free(opfile);
}
int64 OggOpusReader::Read_(int16 *buffer, int64 frames)
{
int16 *tr_buffer = buffer;
int64 tr_count = frames * 2;
while(tr_count > 0)
{
int64 didread = op_read(opfile, tr_buffer, tr_count, NULL);
if(didread == 0)
break;
tr_buffer += didread * 2;
tr_count -= didread * 2;
}
return(frames - (tr_count / 2));
}
bool OggOpusReader::Seek_(int64 frame_offset)
{
op_pcm_seek(opfile, frame_offset);
return(true);
}
int64 OggOpusReader::FrameCount(void)
{
return(op_pcm_total(pvfile, -1));
}

View File

@ -0,0 +1,21 @@
#ifndef __MDFN_AUDIOREADER_OPUS_H
#define __MDFN_AUDIOREADER_OPUS_H
#include <opus/opusfile.h>
class OggOpusReader : public AudioReader
{
public:
OggOpusReader(Stream *fp);
~OggOpusReader();
int64 Read_(int16 *buffer, int64 frames);
bool Seek_(int64 frame_offset);
int64 FrameCount(void);
private:
OggOpus_File *opfile;
Stream *fw;
};
#endif

View File

@ -0,0 +1,434 @@
/* Mednafen - Multi-system Emulator
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <string.h>
#include <sys/types.h>
#include <algorithm>
#include "emuware/emuware.h"
#include "cdromif.h"
#include "CDAccess.h"
#include "general.h"
#include "error.h"
//undo gettext stuff
#define _(X) X
using namespace CDUtility;
enum
{
// Status/Error messages
CDIF_MSG_DONE = 0, // Read -> emu. args: No args.
CDIF_MSG_INFO, // Read -> emu. args: str_message
CDIF_MSG_FATAL_ERROR, // Read -> emu. args: *TODO ARGS*
//
// Command messages.
//
CDIF_MSG_DIEDIEDIE, // Emu -> read
CDIF_MSG_READ_SECTOR, /* Emu -> read
args[0] = lba
*/
CDIF_MSG_EJECT, // Emu -> read, args[0]; 0=insert, 1=eject
};
class CDIF_Message
{
public:
CDIF_Message();
CDIF_Message(unsigned int message_, uint32 arg0 = 0, uint32 arg1 = 0, uint32 arg2 = 0, uint32 arg3 = 0);
CDIF_Message(unsigned int message_, const std::string &str);
~CDIF_Message();
unsigned int message;
uint32 args[4];
void *parg;
std::string str_message;
};
typedef struct
{
bool valid;
bool error;
uint32 lba;
uint8 data[2352 + 96];
} CDIF_Sector_Buffer;
// TODO: prohibit copy constructor
class CDIF_ST : public CDIF
{
public:
CDIF_ST(CDAccess *cda);
virtual ~CDIF_ST();
virtual void HintReadSector(uint32 lba);
virtual bool ReadRawSector(uint8 *buf, uint32 lba);
virtual bool Eject(bool eject_status);
private:
CDAccess *disc_cdaccess;
};
CDIF::CDIF() : UnrecoverableError(false), is_phys_cache(false), DiscEjected(false)
{
}
CDIF::~CDIF()
{
}
CDIF_Message::CDIF_Message()
{
message = 0;
memset(args, 0, sizeof(args));
}
CDIF_Message::CDIF_Message(unsigned int message_, uint32 arg0, uint32 arg1, uint32 arg2, uint32 arg3)
{
message = message_;
args[0] = arg0;
args[1] = arg1;
args[2] = arg2;
args[3] = arg3;
}
CDIF_Message::CDIF_Message(unsigned int message_, const std::string &str)
{
message = message_;
str_message = str;
}
CDIF_Message::~CDIF_Message()
{
}
bool CDIF::ValidateRawSector(uint8 *buf)
{
int mode = buf[12 + 3];
if(mode != 0x1 && mode != 0x2)
return(false);
if(!edc_lec_check_and_correct(buf, mode == 2))
return(false);
return(true);
}
int CDIF::ReadSector(uint8* pBuf, uint32 lba, uint32 nSectors)
{
int ret = 0;
if(UnrecoverableError)
return(false);
while(nSectors--)
{
uint8 tmpbuf[2352 + 96];
if(!ReadRawSector(tmpbuf, lba))
{
puts("CDIF Raw Read error");
return(FALSE_0);
}
if(!ValidateRawSector(tmpbuf))
{
printf(_("Uncorrectable data at sector %d"), lba);
return(false);
}
const int mode = tmpbuf[12 + 3];
if(!ret)
ret = mode;
if(mode == 1)
{
memcpy(pBuf, &tmpbuf[12 + 4], 2048);
}
else if(mode == 2)
{
memcpy(pBuf, &tmpbuf[12 + 4 + 8], 2048);
}
else
{
printf("CDIF_ReadSector() invalid sector type at LBA=%u\n", (unsigned int)lba);
return(false);
}
pBuf += 2048;
lba++;
}
return(ret);
}
//
//
// Single-threaded implementation follows.
//
//
CDIF_ST::CDIF_ST(CDAccess *cda) : disc_cdaccess(cda)
{
//puts("***WARNING USING SINGLE-THREADED CD READER***");
is_phys_cache = disc_cdaccess->Is_Physical();
UnrecoverableError = false;
DiscEjected = false;
disc_cdaccess->Read_TOC(&disc_toc);
if(disc_toc.first_track < 1 || disc_toc.last_track > 99 || disc_toc.first_track > disc_toc.last_track)
{
throw(MDFN_Error(0, _("TOC first(%d)/last(%d) track numbers bad."), disc_toc.first_track, disc_toc.last_track));
}
}
CDIF_ST::~CDIF_ST()
{
if(disc_cdaccess)
{
delete disc_cdaccess;
disc_cdaccess = NULL;
}
}
void CDIF_ST::HintReadSector(uint32 lba)
{
// TODO: disc_cdaccess seek hint? (probably not, would require asynchronousitycamel)
}
bool CDIF_ST::ReadRawSector(uint8 *buf, uint32 lba)
{
if(UnrecoverableError)
{
memset(buf, 0, 2352 + 96);
return(false);
}
try
{
disc_cdaccess->Read_Raw_Sector(buf, lba);
}
catch(std::exception &e)
{
printf(_("Sector %u read error: %s"), lba, e.what());
memset(buf, 0, 2352 + 96);
return(false);
}
return(true);
}
bool CDIF_ST::Eject(bool eject_status)
{
if(UnrecoverableError)
return(false);
try
{
if(eject_status != DiscEjected)
{
disc_cdaccess->Eject(eject_status);
// Set after ->Eject(), since it might throw an exception.
DiscEjected = -1; // For if TOC reading fails or there's something horribly wrong with the disc.
if(!eject_status) // Re-read the TOC
{
disc_cdaccess->Read_TOC(&disc_toc);
if(disc_toc.first_track < 1 || disc_toc.last_track > 99 || disc_toc.first_track > disc_toc.last_track)
{
throw(MDFN_Error(0, _("TOC first(%d)/last(%d) track numbers bad."), disc_toc.first_track, disc_toc.last_track));
}
}
DiscEjected = eject_status;
}
}
catch(std::exception &e)
{
printf("%s", e.what());
return(false);
}
return(true);
}
class CDIF_Stream_Thing : public Stream
{
public:
CDIF_Stream_Thing(CDIF *cdintf_arg, uint32 lba_arg, uint32 sector_count_arg);
~CDIF_Stream_Thing();
virtual uint64 attributes(void) override;
virtual uint64 read(void *data, uint64 count, bool error_on_eos = true) override;
virtual void write(const void *data, uint64 count) override;
virtual void truncate(uint64 length) override;
virtual void seek(int64 offset, int whence) override;
virtual uint64 tell(void) override;
virtual uint64 size(void) override;
virtual void flush(void) override;
virtual void close(void) override;
private:
CDIF *cdintf;
const uint32 start_lba;
const uint32 sector_count;
int64 position;
};
CDIF_Stream_Thing::CDIF_Stream_Thing(CDIF *cdintf_arg, uint32 start_lba_arg, uint32 sector_count_arg) : cdintf(cdintf_arg), start_lba(start_lba_arg), sector_count(sector_count_arg)
{
}
CDIF_Stream_Thing::~CDIF_Stream_Thing()
{
}
uint64 CDIF_Stream_Thing::attributes(void)
{
return(ATTRIBUTE_READABLE | ATTRIBUTE_SEEKABLE);
}
uint64 CDIF_Stream_Thing::read(void *data, uint64 count, bool error_on_eos)
{
if(count > (((uint64)sector_count * 2048) - position))
{
if(error_on_eos)
{
throw MDFN_Error(0, "EOF");
}
count = ((uint64)sector_count * 2048) - position;
}
if(!count)
return(0);
for(uint64 rp = position; rp < (position + count); rp = (rp &~ 2047) + 2048)
{
uint8 buf[2048];
if(!cdintf->ReadSector(buf, start_lba + (rp / 2048), 1))
{
throw MDFN_Error(ErrnoHolder(EIO));
}
//::printf("Meow: %08llx -- %08llx\n", count, (rp - position) + std::min<uint64>(2048 - (rp & 2047), count - (rp - position)));
memcpy((uint8*)data + (rp - position), buf + (rp & 2047), std::min<uint64>(2048 - (rp & 2047), count - (rp - position)));
}
position += count;
return count;
}
void CDIF_Stream_Thing::write(const void *data, uint64 count)
{
throw MDFN_Error(ErrnoHolder(EBADF));
}
void CDIF_Stream_Thing::truncate(uint64 length)
{
throw MDFN_Error(ErrnoHolder(EBADF));
}
void CDIF_Stream_Thing::seek(int64 offset, int whence)
{
int64 new_position;
switch(whence)
{
default:
throw MDFN_Error(ErrnoHolder(EINVAL));
break;
case SEEK_SET:
new_position = offset;
break;
case SEEK_CUR:
new_position = position + offset;
break;
case SEEK_END:
new_position = ((int64)sector_count * 2048) + offset;
break;
}
if(new_position < 0 || new_position > ((int64)sector_count * 2048))
throw MDFN_Error(ErrnoHolder(EINVAL));
position = new_position;
}
uint64 CDIF_Stream_Thing::tell(void)
{
return position;
}
uint64 CDIF_Stream_Thing::size(void)
{
return(sector_count * 2048);
}
void CDIF_Stream_Thing::flush(void)
{
}
void CDIF_Stream_Thing::close(void)
{
}
Stream *CDIF::MakeStream(uint32 lba, uint32 sector_count)
{
return new CDIF_Stream_Thing(this, lba, sector_count);
}
CDIF *CDIF_Open(const std::string& path, const bool is_device, bool image_memcache)
{
CDAccess *cda = cdaccess_open_image(path, image_memcache);
return new CDIF_ST(cda);
}

View File

@ -0,0 +1,70 @@
/* Mednafen - Multi-system Emulator
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __MDFN_CDROM_CDROMIF_H
#define __MDFN_CDROM_CDROMIF_H
#include "CDUtility.h"
#include "stream.h"
#include <queue>
typedef CDUtility::TOC CD_TOC;
class CDIF
{
public:
CDIF();
virtual ~CDIF();
inline void ReadTOC(CDUtility::TOC *read_target)
{
*read_target = disc_toc;
}
virtual void HintReadSector(uint32 lba) = 0;
virtual bool ReadRawSector(uint8 *buf, uint32 lba) = 0;
// Call for mode 1 or mode 2 form 1 only.
bool ValidateRawSector(uint8 *buf);
// Utility/Wrapped functions
// Reads mode 1 and mode2 form 1 sectors(2048 bytes per sector returned)
// Will return the type(1, 2) of the first sector read to the buffer supplied, 0 on error
int ReadSector(uint8* pBuf, uint32 lba, uint32 nSectors);
// Return true if operation succeeded or it was a NOP(either due to not being implemented, or the current status matches eject_status).
// Returns false on failure(usually drive error of some kind; not completely fatal, can try again).
virtual bool Eject(bool eject_status) = 0;
inline bool IsPhysical(void) { return(is_phys_cache); }
// For Mode 1, or Mode 2 Form 1.
// No reference counting or whatever is done, so if you destroy the CDIF object before you destroy the returned Stream, things will go BOOM.
Stream *MakeStream(uint32 lba, uint32 sector_count);
protected:
bool UnrecoverableError;
bool is_phys_cache;
CDUtility::TOC disc_toc;
int DiscEjected; // 0 = inserted, 1 = ejected, -1 = DRAGONS ATE THE DISC. NOM NOM NOM.
};
CDIF *CDIF_Open(const std::string& path, const bool is_device, bool image_memcache);
#endif

View File

@ -0,0 +1,130 @@
/* dvdisaster: Additional error correction for optical media.
* Copyright (C) 2004-2007 Carsten Gnoerlich.
* Project home page: http://www.dvdisaster.com
* Email: carsten@dvdisaster.com -or- cgnoerlich@fsfe.org
*
* CRC32 code based upon public domain code by Ross Williams (see notes below)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA,
* or direct your browser at http://www.gnu.org.
*/
#include "dvdisaster.h"
/***
*** EDC checksum used in CDROM sectors
***/
/*****************************************************************/
/* */
/* CRC LOOKUP TABLE */
/* ================ */
/* The following CRC lookup table was generated automagically */
/* by the Rocksoft^tm Model CRC Algorithm Table Generation */
/* Program V1.0 using the following model parameters: */
/* */
/* Width : 4 bytes. */
/* Poly : 0x8001801BL */
/* Reverse : TRUE. */
/* */
/* For more information on the Rocksoft^tm Model CRC Algorithm, */
/* see the document titled "A Painless Guide to CRC Error */
/* Detection Algorithms" by Ross Williams */
/* (ross@guest.adelaide.edu.au.). This document is likely to be */
/* in the FTP archive "ftp.adelaide.edu.au/pub/rocksoft". */
/* */
/*****************************************************************/
unsigned long edctable[256] =
{
0x00000000L, 0x90910101L, 0x91210201L, 0x01B00300L,
0x92410401L, 0x02D00500L, 0x03600600L, 0x93F10701L,
0x94810801L, 0x04100900L, 0x05A00A00L, 0x95310B01L,
0x06C00C00L, 0x96510D01L, 0x97E10E01L, 0x07700F00L,
0x99011001L, 0x09901100L, 0x08201200L, 0x98B11301L,
0x0B401400L, 0x9BD11501L, 0x9A611601L, 0x0AF01700L,
0x0D801800L, 0x9D111901L, 0x9CA11A01L, 0x0C301B00L,
0x9FC11C01L, 0x0F501D00L, 0x0EE01E00L, 0x9E711F01L,
0x82012001L, 0x12902100L, 0x13202200L, 0x83B12301L,
0x10402400L, 0x80D12501L, 0x81612601L, 0x11F02700L,
0x16802800L, 0x86112901L, 0x87A12A01L, 0x17302B00L,
0x84C12C01L, 0x14502D00L, 0x15E02E00L, 0x85712F01L,
0x1B003000L, 0x8B913101L, 0x8A213201L, 0x1AB03300L,
0x89413401L, 0x19D03500L, 0x18603600L, 0x88F13701L,
0x8F813801L, 0x1F103900L, 0x1EA03A00L, 0x8E313B01L,
0x1DC03C00L, 0x8D513D01L, 0x8CE13E01L, 0x1C703F00L,
0xB4014001L, 0x24904100L, 0x25204200L, 0xB5B14301L,
0x26404400L, 0xB6D14501L, 0xB7614601L, 0x27F04700L,
0x20804800L, 0xB0114901L, 0xB1A14A01L, 0x21304B00L,
0xB2C14C01L, 0x22504D00L, 0x23E04E00L, 0xB3714F01L,
0x2D005000L, 0xBD915101L, 0xBC215201L, 0x2CB05300L,
0xBF415401L, 0x2FD05500L, 0x2E605600L, 0xBEF15701L,
0xB9815801L, 0x29105900L, 0x28A05A00L, 0xB8315B01L,
0x2BC05C00L, 0xBB515D01L, 0xBAE15E01L, 0x2A705F00L,
0x36006000L, 0xA6916101L, 0xA7216201L, 0x37B06300L,
0xA4416401L, 0x34D06500L, 0x35606600L, 0xA5F16701L,
0xA2816801L, 0x32106900L, 0x33A06A00L, 0xA3316B01L,
0x30C06C00L, 0xA0516D01L, 0xA1E16E01L, 0x31706F00L,
0xAF017001L, 0x3F907100L, 0x3E207200L, 0xAEB17301L,
0x3D407400L, 0xADD17501L, 0xAC617601L, 0x3CF07700L,
0x3B807800L, 0xAB117901L, 0xAAA17A01L, 0x3A307B00L,
0xA9C17C01L, 0x39507D00L, 0x38E07E00L, 0xA8717F01L,
0xD8018001L, 0x48908100L, 0x49208200L, 0xD9B18301L,
0x4A408400L, 0xDAD18501L, 0xDB618601L, 0x4BF08700L,
0x4C808800L, 0xDC118901L, 0xDDA18A01L, 0x4D308B00L,
0xDEC18C01L, 0x4E508D00L, 0x4FE08E00L, 0xDF718F01L,
0x41009000L, 0xD1919101L, 0xD0219201L, 0x40B09300L,
0xD3419401L, 0x43D09500L, 0x42609600L, 0xD2F19701L,
0xD5819801L, 0x45109900L, 0x44A09A00L, 0xD4319B01L,
0x47C09C00L, 0xD7519D01L, 0xD6E19E01L, 0x46709F00L,
0x5A00A000L, 0xCA91A101L, 0xCB21A201L, 0x5BB0A300L,
0xC841A401L, 0x58D0A500L, 0x5960A600L, 0xC9F1A701L,
0xCE81A801L, 0x5E10A900L, 0x5FA0AA00L, 0xCF31AB01L,
0x5CC0AC00L, 0xCC51AD01L, 0xCDE1AE01L, 0x5D70AF00L,
0xC301B001L, 0x5390B100L, 0x5220B200L, 0xC2B1B301L,
0x5140B400L, 0xC1D1B501L, 0xC061B601L, 0x50F0B700L,
0x5780B800L, 0xC711B901L, 0xC6A1BA01L, 0x5630BB00L,
0xC5C1BC01L, 0x5550BD00L, 0x54E0BE00L, 0xC471BF01L,
0x6C00C000L, 0xFC91C101L, 0xFD21C201L, 0x6DB0C300L,
0xFE41C401L, 0x6ED0C500L, 0x6F60C600L, 0xFFF1C701L,
0xF881C801L, 0x6810C900L, 0x69A0CA00L, 0xF931CB01L,
0x6AC0CC00L, 0xFA51CD01L, 0xFBE1CE01L, 0x6B70CF00L,
0xF501D001L, 0x6590D100L, 0x6420D200L, 0xF4B1D301L,
0x6740D400L, 0xF7D1D501L, 0xF661D601L, 0x66F0D700L,
0x6180D800L, 0xF111D901L, 0xF0A1DA01L, 0x6030DB00L,
0xF3C1DC01L, 0x6350DD00L, 0x62E0DE00L, 0xF271DF01L,
0xEE01E001L, 0x7E90E100L, 0x7F20E200L, 0xEFB1E301L,
0x7C40E400L, 0xECD1E501L, 0xED61E601L, 0x7DF0E700L,
0x7A80E800L, 0xEA11E901L, 0xEBA1EA01L, 0x7B30EB00L,
0xE8C1EC01L, 0x7850ED00L, 0x79E0EE00L, 0xE971EF01L,
0x7700F000L, 0xE791F101L, 0xE621F201L, 0x76B0F300L,
0xE541F401L, 0x75D0F500L, 0x7460F600L, 0xE4F1F701L,
0xE381F801L, 0x7310F900L, 0x72A0FA00L, 0xE231FB01L,
0x71C0FC00L, 0xE151FD01L, 0xE0E1FE01L, 0x7070FF00L
};
/*
* CDROM EDC calculation
*/
uint32 EDCCrc32(const unsigned char *data, int len)
{
uint32 crc = 0;
while(len--)
crc = edctable[(crc ^ *data++) & 0xFF] ^ (crc >> 8);
return crc;
}

View File

@ -0,0 +1,173 @@
/* dvdisaster: Additional error correction for optical media.
* Copyright (C) 2004-2007 Carsten Gnoerlich.
* Project home page: http://www.dvdisaster.com
* Email: carsten@dvdisaster.com -or- cgnoerlich@fsfe.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA,
* or direct your browser at http://www.gnu.org.
*/
#ifndef DVDISASTER_H
#define DVDISASTER_H
/* "Dare to be gorgeous and unique.
* But don't ever be cryptic or otherwise unfathomable.
* Make it unforgettably great."
*
* From "A Final Note on Style",
* Amiga Intuition Reference Manual, 1986, p. 231
*/
/***
*** I'm too lazy to mess with #include dependencies.
*** Everything #includeable is rolled up herein...
*/
//#include "../types.h"
#include "emuware/emuware.h"
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <math.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//#include <unistd.h>
/***
*** dvdisaster.c
***/
void PrepareDeadSector(void);
void CreateEcc(void);
void FixEcc(void);
void Verify(void);
uint32 EDCCrc32(const unsigned char*, int);
/***
*** galois.c
***
* This is currently the hardcoded GF(2**8).
* int32 gives abundant space for the GF.
* Squeezing it down to uint8 won't probably gain much,
* so we implement this defensively here.
*
* Note that some performance critical stuff needs to
* be #included from galois-inlines.h
*/
/* Galois field parameters for 8bit symbol Reed-Solomon code */
#define GF_SYMBOLSIZE 8
#define GF_FIELDSIZE (1<<GF_SYMBOLSIZE)
#define GF_FIELDMAX (GF_FIELDSIZE-1)
#define GF_ALPHA0 GF_FIELDMAX
/* Lookup tables for Galois field arithmetic */
typedef struct _GaloisTables
{ int32 gfGenerator; /* GF generator polynomial */
int32 *indexOf; /* log */
int32 *alphaTo; /* inverse log */
int32 *encAlphaTo; /* inverse log optimized for encoder */
} GaloisTables;
/* Lookup and working tables for the ReedSolomon codecs */
typedef struct _ReedSolomonTables
{ GaloisTables *gfTables;/* from above */
int32 *gpoly; /* RS code generator polynomial */
int32 fcr; /* first consecutive root of RS generator polynomial */
int32 primElem; /* primitive field element */
int32 nroots; /* degree of RS generator polynomial */
int32 ndata; /* data bytes per ecc block */
} ReedSolomonTables;
GaloisTables* CreateGaloisTables(int32);
void FreeGaloisTables(GaloisTables*);
ReedSolomonTables *CreateReedSolomonTables(GaloisTables*, int32, int32, int);
void FreeReedSolomonTables(ReedSolomonTables*);
/***
*** l-ec.c
***/
#define N_P_VECTORS 86 /* 43 16bit p vectors */
#define P_VECTOR_SIZE 26 /* using RS(26,24) ECC */
#define N_Q_VECTORS 52 /* 26 16bit q vectors */
#define Q_VECTOR_SIZE 45 /* using RS(45,43) ECC */
#define P_PADDING 229 /* padding values for */
#define Q_PADDING 210 /* shortened RS code */
int PToByteIndex(int, int);
int QToByteIndex(int, int);
void ByteIndexToP(int, int*, int*);
void ByteIndexToQ(int, int*, int*);
void GetPVector(unsigned char*, unsigned char*, int);
void SetPVector(unsigned char*, unsigned char*, int);
void FillPVector(unsigned char*, unsigned char, int);
void AndPVector(unsigned char*, unsigned char, int);
void OrPVector(unsigned char*, unsigned char, int);
void GetQVector(unsigned char*, unsigned char*, int);
void SetQVector(unsigned char*, unsigned char*, int);
void FillQVector(unsigned char*, unsigned char, int);
void AndQVector(unsigned char*, unsigned char, int);
void OrQVector(unsigned char*, unsigned char, int);
int DecodePQ(ReedSolomonTables*, unsigned char*, int, int*, int);
int CountC2Errors(unsigned char*);
/***
*** misc.c
***/
char* sgettext(char*);
char* sgettext_utf8(char*);
int64 uchar_to_int64(unsigned char*);
void int64_to_uchar(unsigned char*, int64);
void CalcSectors(int64, int64*, int*);
/***
*** recover-raw.c
***/
#define CD_RAW_SECTOR_SIZE 2352
#define CD_RAW_C2_SECTOR_SIZE (2352+294) /* main channel plus C2 vector */
int CheckEDC(const unsigned char*, bool);
int CheckMSF(unsigned char*, int);
int ValidateRawSector(unsigned char *frame, bool xaMode);
bool Init_LEC_Correct(void);
void Kill_LEC_Correct(void);
#endif /* DVDISASTER_H */

View File

@ -0,0 +1,40 @@
/* dvdisaster: Additional error correction for optical media.
* Copyright (C) 2004-2007 Carsten Gnoerlich.
* Project home page: http://www.dvdisaster.com
* Email: carsten@dvdisaster.com -or- cgnoerlich@fsfe.org
*
* The Reed-Solomon error correction draws a lot of inspiration - and even code -
* from Phil Karn's excellent Reed-Solomon library: http://www.ka9q.net/code/fec/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA,
* or direct your browser at http://www.gnu.org.
*/
#include "dvdisaster.h"
/*
* The following routine is performance critical.
*/
static inline int mod_fieldmax(int x)
{
while (x >= GF_FIELDMAX)
{
x -= GF_FIELDMAX;
x = (x >> GF_SYMBOLSIZE) + (x & GF_FIELDMAX);
}
return x;
}

View File

@ -0,0 +1,156 @@
/* dvdisaster: Additional error correction for optical media.
* Copyright (C) 2004-2007 Carsten Gnoerlich.
* Project home page: http://www.dvdisaster.com
* Email: carsten@dvdisaster.com -or- cgnoerlich@fsfe.org
*
* The Reed-Solomon error correction draws a lot of inspiration - and even code -
* from Phil Karn's excellent Reed-Solomon library: http://www.ka9q.net/code/fec/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA,
* or direct your browser at http://www.gnu.org.
*/
#include "dvdisaster.h"
#include "galois-inlines.h"
/***
*** Galois field arithmetic.
***
* Calculations are done over the extension field GF(2**n).
* Be careful not to overgeneralize these arithmetics;
* they only work for the case of GF(p**n) with p being prime.
*/
/* Initialize the Galois field tables */
GaloisTables* CreateGaloisTables(int32 gf_generator)
{
GaloisTables *gt = (GaloisTables *)calloc(1, sizeof(GaloisTables));
int32 b,log;
/* Allocate the tables.
The encoder uses a special version of alpha_to which has the mod_fieldmax()
folded into the table. */
gt->gfGenerator = gf_generator;
gt->indexOf = (int32 *)calloc(GF_FIELDSIZE, sizeof(int32));
gt->alphaTo = (int32 *)calloc(GF_FIELDSIZE, sizeof(int32));
gt->encAlphaTo = (int32 *)calloc(2*GF_FIELDSIZE, sizeof(int32));
/* create the log/ilog values */
for(b=1, log=0; log<GF_FIELDMAX; log++)
{ gt->indexOf[b] = log;
gt->alphaTo[log] = b;
b = b << 1;
if(b & GF_FIELDSIZE)
b = b ^ gf_generator;
}
if(b!=1)
{
printf("Failed to create the Galois field log tables!\n");
exit(1);
}
/* we're even closed using infinity (makes things easier) */
gt->indexOf[0] = GF_ALPHA0; /* log(0) = inf */
gt->alphaTo[GF_ALPHA0] = 0; /* and the other way around */
for(b=0; b<2*GF_FIELDSIZE; b++)
gt->encAlphaTo[b] = gt->alphaTo[mod_fieldmax(b)];
return gt;
}
void FreeGaloisTables(GaloisTables *gt)
{
if(gt->indexOf) free(gt->indexOf);
if(gt->alphaTo) free(gt->alphaTo);
if(gt->encAlphaTo) free(gt->encAlphaTo);
free(gt);
}
/***
*** Create the the Reed-Solomon generator polynomial
*** and some auxiliary data structures.
*/
ReedSolomonTables *CreateReedSolomonTables(GaloisTables *gt,
int32 first_consecutive_root,
int32 prim_elem,
int nroots_in)
{ ReedSolomonTables *rt = (ReedSolomonTables *)calloc(1, sizeof(ReedSolomonTables));
int32 i,j,root;
rt->gfTables = gt;
rt->fcr = first_consecutive_root;
rt->primElem = prim_elem;
rt->nroots = nroots_in;
rt->ndata = GF_FIELDMAX - rt->nroots;
rt->gpoly = (int32 *)calloc((rt->nroots+1), sizeof(int32));
/* Create the RS code generator polynomial */
rt->gpoly[0] = 1;
for(i=0, root=first_consecutive_root*prim_elem; i<rt->nroots; i++, root+=prim_elem)
{ rt->gpoly[i+1] = 1;
/* Multiply gpoly by alpha**(root+x) */
for(j=i; j>0; j--)
{
if(rt->gpoly[j] != 0)
rt->gpoly[j] = rt->gpoly[j-1] ^ gt->alphaTo[mod_fieldmax(gt->indexOf[rt->gpoly[j]] + root)];
else
rt->gpoly[j] = rt->gpoly[j-1];
}
rt->gpoly[0] = gt->alphaTo[mod_fieldmax(gt->indexOf[rt->gpoly[0]] + root)];
}
/* Store the polynomials index for faster encoding */
for(i=0; i<=rt->nroots; i++)
rt->gpoly[i] = gt->indexOf[rt->gpoly[i]];
#if 0
/* for the precalculated unrolled loops only */
for(i=gt->nroots-1; i>0; i--)
PrintCLI(
" par_idx[((++spk)&%d)] ^= enc_alpha_to[feedback + %3d];\n",
nroots-1,gt->gpoly[i]);
PrintCLI(" par_idx[sp] = enc_alpha_to[feedback + %3d];\n",
gt->gpoly[0]);
#endif
return rt;
}
void FreeReedSolomonTables(ReedSolomonTables *rt)
{
if(rt->gpoly) free(rt->gpoly);
free(rt);
}

View File

@ -0,0 +1,478 @@
/* dvdisaster: Additional error correction for optical media.
* Copyright (C) 2004-2007 Carsten Gnoerlich.
* Project home page: http://www.dvdisaster.com
* Email: carsten@dvdisaster.com -or- cgnoerlich@fsfe.org
*
* The Reed-Solomon error correction draws a lot of inspiration - and even code -
* from Phil Karn's excellent Reed-Solomon library: http://www.ka9q.net/code/fec/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA,
* or direct your browser at http://www.gnu.org.
*/
#include "dvdisaster.h"
#include "galois-inlines.h"
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
/***
*** Mapping between cd frame and parity vectors
***/
/*
* Mapping of frame bytes to P/Q Vectors
*/
int PToByteIndex(int p, int i)
{ return 12 + p + i*86;
}
void ByteIndexToP(int b, int *p, int *i)
{ *p = (b-12)%86;
*i = (b-12)/86;
}
int QToByteIndex(int q, int i)
{ int offset = 12 + (q & 1);
if(i == 43) return 2248+q;
if(i == 44) return 2300+q;
q&=~1;
return offset + (q*43 + i*88) % 2236;
}
void ByteIndexToQ(int b, int *q, int *i)
{ int x,y,offset;
if(b >= 2300)
{ *i = 44;
*q = (b-2300);
return;
}
if(b >= 2248)
{ *i = 43;
*q = (b-2248);
return;
}
offset = b&1;
b = (b-12)/2;
x = b/43;
y = (b-(x*43))%26;
*i = b-(x*43);
*q = 2*((x+26-y)%26)+offset;
}
/*
* There are 86 vectors of P-parity, yielding a RS(26,24) code.
*/
void GetPVector(unsigned char *frame, unsigned char *data, int n)
{ int i;
int w_idx = n+12;
for(i=0; i<26; i++, w_idx+=86)
data[i] = frame[w_idx];
}
void SetPVector(unsigned char *frame, unsigned char *data, int n)
{ int i;
int w_idx = n+12;
for(i=0; i<26; i++, w_idx+=86)
frame[w_idx] = data[i];
}
void FillPVector(unsigned char *frame, unsigned char data, int n)
{ int i;
int w_idx = n+12;
for(i=0; i<26; i++, w_idx+=86)
frame[w_idx] = data;
}
void OrPVector(unsigned char *frame, unsigned char value, int n)
{ int i;
int w_idx = n+12;
for(i=0; i<26; i++, w_idx+=86)
frame[w_idx] |= value;
}
void AndPVector(unsigned char *frame, unsigned char value, int n)
{ int i;
int w_idx = n+12;
for(i=0; i<26; i++, w_idx+=86)
frame[w_idx] &= value;
}
/*
* There are 52 vectors of Q-parity, yielding a RS(45,43) code.
*/
void GetQVector(unsigned char *frame, unsigned char *data, int n)
{ int offset = 12 + (n & 1);
int w_idx = (n&~1) * 43;
int i;
for(i=0; i<43; i++, w_idx+=88)
data[i] = frame[(w_idx % 2236) + offset];
data[43] = frame[2248 + n];
data[44] = frame[2300 + n];
}
void SetQVector(unsigned char *frame, unsigned char *data, int n)
{ int offset = 12 + (n & 1);
int w_idx = (n&~1) * 43;
int i;
for(i=0; i<43; i++, w_idx+=88)
frame[(w_idx % 2236) + offset] = data[i];
frame[2248 + n] = data[43];
frame[2300 + n] = data[44];
}
void FillQVector(unsigned char *frame, unsigned char data, int n)
{ int offset = 12 + (n & 1);
int w_idx = (n&~1) * 43;
int i;
for(i=0; i<43; i++, w_idx+=88)
frame[(w_idx % 2236) + offset] = data;
frame[2248 + n] = data;
frame[2300 + n] = data;
}
void OrQVector(unsigned char *frame, unsigned char data, int n)
{ int offset = 12 + (n & 1);
int w_idx = (n&~1) * 43;
int i;
for(i=0; i<43; i++, w_idx+=88)
frame[(w_idx % 2236) + offset] |= data;
frame[2248 + n] |= data;
frame[2300 + n] |= data;
}
void AndQVector(unsigned char *frame, unsigned char data, int n)
{ int offset = 12 + (n & 1);
int w_idx = (n&~1) * 43;
int i;
for(i=0; i<43; i++, w_idx+=88)
frame[(w_idx % 2236) + offset] &= data;
frame[2248 + n] &= data;
frame[2300 + n] &= data;
}
/***
*** C2 error counting
***/
int CountC2Errors(unsigned char *frame)
{ int i,count = 0;
frame += 2352;
for(i=0; i<294; i++, frame++)
{ if(*frame & 0x01) count++;
if(*frame & 0x02) count++;
if(*frame & 0x04) count++;
if(*frame & 0x08) count++;
if(*frame & 0x10) count++;
if(*frame & 0x20) count++;
if(*frame & 0x40) count++;
if(*frame & 0x80) count++;
}
return count;
}
/***
*** L-EC error correction for CD raw data sectors
***/
/*
* These could be used from ReedSolomonTables,
* but hardcoding them is faster.
*/
#define NROOTS 2
#define LEC_FIRST_ROOT 0 //GF_ALPHA0
#define LEC_PRIM_ELEM 1
#define LEC_PRIMTH_ROOT 1
/*
* Calculate the error syndrome
*/
int DecodePQ(ReedSolomonTables *rt, unsigned char *data, int padding,
int *erasure_list, int erasure_count)
{ GaloisTables *gt = rt->gfTables;
int syndrome[NROOTS];
int lambda[NROOTS+1];
int omega[NROOTS+1];
int b[NROOTS+1];
int reg[NROOTS+1];
int root[NROOTS];
int loc[NROOTS];
int syn_error;
int deg_lambda,lambda_roots;
int deg_omega;
int shortened_size = GF_FIELDMAX - padding;
int corrected = 0;
int i,j,k;
int r,el;
/*** Form the syndromes: Evaluate data(x) at roots of g(x) */
for(i=0; i<NROOTS; i++)
syndrome[i] = data[0];
for(j=1; j<shortened_size; j++)
for(i=0; i<NROOTS; i++)
if(syndrome[i] == 0)
syndrome[i] = data[j];
else syndrome[i] = data[j] ^ gt->alphaTo[mod_fieldmax(gt->indexOf[syndrome[i]]
+ (LEC_FIRST_ROOT+i)*LEC_PRIM_ELEM)];
/*** Convert syndrome to index form, check for nonzero condition. */
syn_error = 0;
for(i=0; i<NROOTS; i++)
{ syn_error |= syndrome[i];
syndrome[i] = gt->indexOf[syndrome[i]];
}
/*** If the syndrome is zero, everything is fine. */
if(!syn_error)
return 0;
/*** Initialize lambda to be the erasure locator polynomial */
lambda[0] = 1;
lambda[1] = lambda[2] = 0;
erasure_list[0] += padding;
erasure_list[1] += padding;
if(erasure_count > 2) /* sanity check */
erasure_count = 0;
if(erasure_count > 0)
{ lambda[1] = gt->alphaTo[mod_fieldmax(LEC_PRIM_ELEM*(GF_FIELDMAX-1-erasure_list[0]))];
for(i=1; i<erasure_count; i++)
{ int u = mod_fieldmax(LEC_PRIM_ELEM*(GF_FIELDMAX-1-erasure_list[i]));
for(j=i+1; j>0; j--)
{ int tmp = gt->indexOf[lambda[j-1]];
if(tmp != GF_ALPHA0)
lambda[j] ^= gt->alphaTo[mod_fieldmax(u + tmp)];
}
}
}
for(i=0; i<NROOTS+1; i++)
b[i] = gt->indexOf[lambda[i]];
/*** Berlekamp-Massey algorithm to determine error+erasure locator polynomial */
r = erasure_count; /* r is the step number */
el = erasure_count;
/* Compute discrepancy at the r-th step in poly-form */
while(++r <= NROOTS)
{ int discr_r = 0;
for(i=0; i<r; i++)
if((lambda[i] != 0) && (syndrome[r-i-1] != GF_ALPHA0))
discr_r ^= gt->alphaTo[mod_fieldmax(gt->indexOf[lambda[i]] + syndrome[r-i-1])];
discr_r = gt->indexOf[discr_r];
if(discr_r == GF_ALPHA0)
{ /* B(x) = x*B(x) */
memmove(b+1, b, NROOTS*sizeof(b[0]));
b[0] = GF_ALPHA0;
}
else
{ int t[NROOTS+1];
/* T(x) = lambda(x) - discr_r*x*b(x) */
t[0] = lambda[0];
for(i=0; i<NROOTS; i++)
{ if(b[i] != GF_ALPHA0)
t[i+1] = lambda[i+1] ^ gt->alphaTo[mod_fieldmax(discr_r + b[i])];
else t[i+1] = lambda[i+1];
}
if(2*el <= r+erasure_count-1)
{ el = r + erasure_count - el;
/* B(x) <-- inv(discr_r) * lambda(x) */
for(i=0; i<=NROOTS; i++)
b[i] = (lambda[i] == 0) ? GF_ALPHA0
: mod_fieldmax(gt->indexOf[lambda[i]] - discr_r + GF_FIELDMAX);
}
else
{ /* 2 lines below: B(x) <-- x*B(x) */
memmove(b+1, b, NROOTS*sizeof(b[0]));
b[0] = GF_ALPHA0;
}
memcpy(lambda, t, (NROOTS+1)*sizeof(t[0]));
}
}
/*** Convert lambda to index form and compute deg(lambda(x)) */
deg_lambda = 0;
for(i=0; i<NROOTS+1; i++)
{ lambda[i] = gt->indexOf[lambda[i]];
if(lambda[i] != GF_ALPHA0)
deg_lambda = i;
}
/*** Find roots of the error+erasure locator polynomial by Chien search */
memcpy(reg+1, lambda+1, NROOTS*sizeof(reg[0]));
lambda_roots = 0; /* Number of roots of lambda(x) */
for(i=1, k=LEC_PRIMTH_ROOT-1; i<=GF_FIELDMAX; i++, k=mod_fieldmax(k+LEC_PRIMTH_ROOT))
{ int q=1; /* lambda[0] is always 0 */
for(j=deg_lambda; j>0; j--)
{ if(reg[j] != GF_ALPHA0)
{ reg[j] = mod_fieldmax(reg[j] + j);
q ^= gt->alphaTo[reg[j]];
}
}
if(q != 0) continue; /* Not a root */
/* store root in index-form and the error location number */
root[lambda_roots] = i;
loc[lambda_roots] = k;
/* If we've already found max possible roots, abort the search to save time */
if(++lambda_roots == deg_lambda) break;
}
/* deg(lambda) unequal to number of roots => uncorrectable error detected
This is not reliable for very small numbers of roots, e.g. nroots = 2 */
if(deg_lambda != lambda_roots)
{ return -1;
}
/* Compute err+eras evaluator poly omega(x) = syn(x)*lambda(x)
(modulo x**nroots). in index form. Also find deg(omega). */
deg_omega = deg_lambda-1;
for(i=0; i<=deg_omega; i++)
{ int tmp = 0;
for(j=i; j>=0; j--)
{ if((syndrome[i - j] != GF_ALPHA0) && (lambda[j] != GF_ALPHA0))
tmp ^= gt->alphaTo[mod_fieldmax(syndrome[i - j] + lambda[j])];
}
omega[i] = gt->indexOf[tmp];
}
/* Compute error values in poly-form.
num1 = omega(inv(X(l))),
num2 = inv(X(l))**(FIRST_ROOT-1) and
den = lambda_pr(inv(X(l))) all in poly-form. */
for(j=lambda_roots-1; j>=0; j--)
{ int num1 = 0;
int num2;
int den;
int location = loc[j];
for(i=deg_omega; i>=0; i--)
{ if(omega[i] != GF_ALPHA0)
num1 ^= gt->alphaTo[mod_fieldmax(omega[i] + i * root[j])];
}
num2 = gt->alphaTo[mod_fieldmax(root[j] * (LEC_FIRST_ROOT - 1) + GF_FIELDMAX)];
den = 0;
/* lambda[i+1] for i even is the formal derivative lambda_pr of lambda[i] */
for(i=MIN(deg_lambda, NROOTS-1) & ~1; i>=0; i-=2)
{ if(lambda[i+1] != GF_ALPHA0)
den ^= gt->alphaTo[mod_fieldmax(lambda[i+1] + i * root[j])];
}
/* Apply error to data */
if(num1 != 0 && location >= padding)
{
corrected++;
data[location-padding] ^= gt->alphaTo[mod_fieldmax(gt->indexOf[num1] + gt->indexOf[num2]
+ GF_FIELDMAX - gt->indexOf[den])];
/* If no erasures were given, at most one error was corrected.
Return its position in erasure_list[0]. */
if(!erasure_count)
erasure_list[0] = location-padding;
}
#if 1
else return -3;
#endif
}
/*** Form the syndromes: Evaluate data(x) at roots of g(x) */
for(i=0; i<NROOTS; i++)
syndrome[i] = data[0];
for(j=1; j<shortened_size; j++)
for(i=0; i<NROOTS; i++)
{ if(syndrome[i] == 0)
syndrome[i] = data[j];
else syndrome[i] = data[j] ^ gt->alphaTo[mod_fieldmax(gt->indexOf[syndrome[i]]
+ (LEC_FIRST_ROOT+i)*LEC_PRIM_ELEM)];
}
/*** Convert syndrome to index form, check for nonzero condition. */
#if 1
for(i=0; i<NROOTS; i++)
if(syndrome[i])
return -2;
#endif
return corrected;
}

691
psx/mednadisc/cdrom/lec.cpp Normal file
View File

@ -0,0 +1,691 @@
/* cdrdao - write audio CD-Rs in disc-at-once mode
*
* Copyright (C) 1998-2002 Andreas Mueller <andreas@daneb.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <assert.h>
#include <sys/types.h>
#include "lec.h"
#define GF8_PRIM_POLY 0x11d /* x^8 + x^4 + x^3 + x^2 + 1 */
#define EDC_POLY 0x8001801b /* (x^16 + x^15 + x^2 + 1) (x^16 + x^2 + x + 1) */
#define LEC_HEADER_OFFSET 12
#define LEC_DATA_OFFSET 16
#define LEC_MODE1_DATA_LEN 2048
#define LEC_MODE1_EDC_OFFSET 2064
#define LEC_MODE1_INTERMEDIATE_OFFSET 2068
#define LEC_MODE1_P_PARITY_OFFSET 2076
#define LEC_MODE1_Q_PARITY_OFFSET 2248
#define LEC_MODE2_FORM1_DATA_LEN (2048+8)
#define LEC_MODE2_FORM1_EDC_OFFSET 2072
#define LEC_MODE2_FORM2_DATA_LEN (2324+8)
#define LEC_MODE2_FORM2_EDC_OFFSET 2348
typedef u_int8_t gf8_t;
static u_int8_t GF8_LOG[256];
static gf8_t GF8_ILOG[256];
static const class Gf8_Q_Coeffs_Results_01 {
private:
u_int16_t table[43][256];
public:
Gf8_Q_Coeffs_Results_01();
~Gf8_Q_Coeffs_Results_01() {}
const u_int16_t *operator[] (int i) const { return &table[i][0]; }
operator const u_int16_t *() const { return &table[0][0]; }
} CF8_Q_COEFFS_RESULTS_01;
static const class CrcTable {
private:
u_int32_t table[256];
public:
CrcTable();
~CrcTable() {}
u_int32_t operator[](int i) const { return table[i]; }
operator const u_int32_t *() const { return table; }
} CRCTABLE;
static const class ScrambleTable {
private:
u_int8_t table[2340];
public:
ScrambleTable();
~ScrambleTable() {}
u_int8_t operator[](int i) const { return table[i]; }
operator const u_int8_t *() const { return table; }
} SCRAMBLE_TABLE;
/* Creates the logarithm and inverse logarithm table that is required
* for performing multiplication in the GF(8) domain.
*/
static void gf8_create_log_tables()
{
u_int8_t log;
u_int16_t b;
for (b = 0; b <= 255; b++) {
GF8_LOG[b] = 0;
GF8_ILOG[b] = 0;
}
b = 1;
for (log = 0; log < 255; log++) {
GF8_LOG[(u_int8_t)b] = log;
GF8_ILOG[log] = (u_int8_t)b;
b <<= 1;
if ((b & 0x100) != 0)
b ^= GF8_PRIM_POLY;
}
}
/* Addition in the GF(8) domain: just the XOR of the values.
*/
#define gf8_add(a, b) (a) ^ (b)
/* Multiplication in the GF(8) domain: add the logarithms (modulo 255)
* and return the inverse logarithm. Not used!
*/
#if 0
static gf8_t gf8_mult(gf8_t a, gf8_t b)
{
int16_t sum;
if (a == 0 || b == 0)
return 0;
sum = GF8_LOG[a] + GF8_LOG[b];
if (sum >= 255)
sum -= 255;
return GF8_ILOG[sum];
}
#endif
/* Division in the GF(8) domain: Like multiplication but logarithms a
* subtracted.
*/
static gf8_t gf8_div(gf8_t a, gf8_t b)
{
int16_t sum;
assert(b != 0);
if (a == 0)
return 0;
sum = GF8_LOG[a] - GF8_LOG[b];
if (sum < 0)
sum += 255;
return GF8_ILOG[sum];
}
Gf8_Q_Coeffs_Results_01::Gf8_Q_Coeffs_Results_01()
{
int i, j;
u_int16_t c;
gf8_t GF8_COEFFS_HELP[2][45];
u_int8_t GF8_Q_COEFFS[2][45];
gf8_create_log_tables();
/* build matrix H:
* 1 1 ... 1 1
* a^44 a^43 ... a^1 a^0
*
*
*/
for (j = 0; j < 45; j++) {
GF8_COEFFS_HELP[0][j] = 1; /* e0 */
GF8_COEFFS_HELP[1][j] = GF8_ILOG[44-j]; /* e1 */
}
/* resolve equation system for parity byte 0 and 1 */
/* e1' = e1 + e0 */
for (j = 0; j < 45; j++) {
GF8_Q_COEFFS[1][j] = gf8_add(GF8_COEFFS_HELP[1][j],
GF8_COEFFS_HELP[0][j]);
}
/* e1'' = e1' / (a^1 + 1) */
for (j = 0; j < 45; j++) {
GF8_Q_COEFFS[1][j] = gf8_div(GF8_Q_COEFFS[1][j], GF8_Q_COEFFS[1][43]);
}
/* e0' = e0 + e1 / a^1 */
for (j = 0; j < 45; j++) {
GF8_Q_COEFFS[0][j] = gf8_add(GF8_COEFFS_HELP[0][j],
gf8_div(GF8_COEFFS_HELP[1][j],
GF8_ILOG[1]));
}
/* e0'' = e0' / (1 + 1 / a^1) */
for (j = 0; j < 45; j++) {
GF8_Q_COEFFS[0][j] = gf8_div(GF8_Q_COEFFS[0][j], GF8_Q_COEFFS[0][44]);
}
/*
* Compute the products of 0..255 with all of the Q coefficients in
* advance. When building the scalar product between the data vectors
* and the P/Q vectors the individual products can be looked up in
* this table
*
* The P parity coefficients are just a subset of the Q coefficients so
* that we do not need to create a separate table for them.
*/
for (j = 0; j < 43; j++) {
table[j][0] = 0;
for (i = 1; i < 256; i++) {
c = GF8_LOG[i] + GF8_LOG[GF8_Q_COEFFS[0][j]];
if (c >= 255) c -= 255;
table[j][i] = GF8_ILOG[c];
c = GF8_LOG[i] + GF8_LOG[GF8_Q_COEFFS[1][j]];
if (c >= 255) c -= 255;
table[j][i] |= GF8_ILOG[c]<<8;
}
}
}
/* Reverses the bits in 'd'. 'bits' defines the bit width of 'd'.
*/
static u_int32_t mirror_bits(u_int32_t d, int bits)
{
int i;
u_int32_t r = 0;
for (i = 0; i < bits; i++) {
r <<= 1;
if ((d & 0x1) != 0)
r |= 0x1;
d >>= 1;
}
return r;
}
/* Build the CRC lookup table for EDC_POLY poly. The CRC is 32 bit wide
* and reversed (i.e. the bit stream is divided by the EDC_POLY with the
* LSB first order).
*/
CrcTable::CrcTable ()
{
u_int32_t i, j;
u_int32_t r;
for (i = 0; i < 256; i++) {
r = mirror_bits(i, 8);
r <<= 24;
for (j = 0; j < 8; j++) {
if ((r & 0x80000000) != 0) {
r <<= 1;
r ^= EDC_POLY;
}
else {
r <<= 1;
}
}
r = mirror_bits(r, 32);
table[i] = r;
}
}
/* Calculates the CRC of given data with given lengths based on the
* table lookup algorithm.
*/
static u_int32_t calc_edc(u_int8_t *data, int len)
{
u_int32_t crc = 0;
while (len--) {
crc = CRCTABLE[(int)(crc ^ *data++) & 0xff] ^ (crc >> 8);
}
return crc;
}
/* Build the scramble table as defined in the yellow book. The bytes
12 to 2351 of a sector will be XORed with the data of this table.
*/
ScrambleTable::ScrambleTable()
{
u_int16_t i, j;
u_int16_t reg = 1;
u_int8_t d;
for (i = 0; i < 2340; i++) {
d = 0;
for (j = 0; j < 8; j++) {
d >>= 1;
if ((reg & 0x1) != 0)
d |= 0x80;
if ((reg & 0x1) != ((reg >> 1) & 0x1)) {
reg >>= 1;
reg |= 0x4000; /* 15-bit register */
}
else {
reg >>= 1;
}
}
table[i] = d;
}
}
/* Calc EDC for a MODE 1 sector
*/
static void calc_mode1_edc(u_int8_t *sector)
{
u_int32_t crc = calc_edc(sector, LEC_MODE1_DATA_LEN + 16);
sector[LEC_MODE1_EDC_OFFSET] = crc & 0xffL;
sector[LEC_MODE1_EDC_OFFSET + 1] = (crc >> 8) & 0xffL;
sector[LEC_MODE1_EDC_OFFSET + 2] = (crc >> 16) & 0xffL;
sector[LEC_MODE1_EDC_OFFSET + 3] = (crc >> 24) & 0xffL;
}
/* Calc EDC for a XA form 1 sector
*/
static void calc_mode2_form1_edc(u_int8_t *sector)
{
u_int32_t crc = calc_edc(sector + LEC_DATA_OFFSET,
LEC_MODE2_FORM1_DATA_LEN);
sector[LEC_MODE2_FORM1_EDC_OFFSET] = crc & 0xffL;
sector[LEC_MODE2_FORM1_EDC_OFFSET + 1] = (crc >> 8) & 0xffL;
sector[LEC_MODE2_FORM1_EDC_OFFSET + 2] = (crc >> 16) & 0xffL;
sector[LEC_MODE2_FORM1_EDC_OFFSET + 3] = (crc >> 24) & 0xffL;
}
/* Calc EDC for a XA form 2 sector
*/
static void calc_mode2_form2_edc(u_int8_t *sector)
{
u_int32_t crc = calc_edc(sector + LEC_DATA_OFFSET,
LEC_MODE2_FORM2_DATA_LEN);
sector[LEC_MODE2_FORM2_EDC_OFFSET] = crc & 0xffL;
sector[LEC_MODE2_FORM2_EDC_OFFSET + 1] = (crc >> 8) & 0xffL;
sector[LEC_MODE2_FORM2_EDC_OFFSET + 2] = (crc >> 16) & 0xffL;
sector[LEC_MODE2_FORM2_EDC_OFFSET + 3] = (crc >> 24) & 0xffL;
}
/* Writes the sync pattern to the given sector.
*/
static void set_sync_pattern(u_int8_t *sector)
{
sector[0] = 0;
sector[1] = sector[2] = sector[3] = sector[4] = sector[5] =
sector[6] = sector[7] = sector[8] = sector[9] = sector[10] = 0xff;
sector[11] = 0;
}
static u_int8_t bin2bcd(u_int8_t b)
{
return (((b/10) << 4) & 0xf0) | ((b%10) & 0x0f);
}
/* Builds the sector header.
*/
static void set_sector_header(u_int8_t mode, u_int32_t adr, u_int8_t *sector)
{
sector[LEC_HEADER_OFFSET] = bin2bcd(adr / (60*75));
sector[LEC_HEADER_OFFSET + 1] = bin2bcd((adr / 75) % 60);
sector[LEC_HEADER_OFFSET + 2] = bin2bcd(adr % 75);
sector[LEC_HEADER_OFFSET + 3] = mode;
}
/* Calculate the P parities for the sector.
* The 43 P vectors of length 24 are combined with the GF8_P_COEFFS.
*/
static void calc_P_parity(u_int8_t *sector)
{
int i, j;
u_int16_t p01_msb, p01_lsb;
u_int8_t *p_lsb_start;
u_int8_t *p_lsb;
u_int8_t *p0, *p1;
u_int8_t d0,d1;
p_lsb_start = sector + LEC_HEADER_OFFSET;
p1 = sector + LEC_MODE1_P_PARITY_OFFSET;
p0 = sector + LEC_MODE1_P_PARITY_OFFSET + 2 * 43;
for (i = 0; i <= 42; i++) {
p_lsb = p_lsb_start;
p01_lsb = p01_msb = 0;
for (j = 19; j <= 42; j++) {
d0 = *p_lsb;
d1 = *(p_lsb+1);
p01_lsb ^= CF8_Q_COEFFS_RESULTS_01[j][d0];
p01_msb ^= CF8_Q_COEFFS_RESULTS_01[j][d1];
p_lsb += 2 * 43;
}
*p0 = p01_lsb;
*(p0 + 1) = p01_msb;
*p1 = p01_lsb>>8;
*(p1 + 1) = p01_msb>>8;
p0 += 2;
p1 += 2;
p_lsb_start += 2;
}
}
/* Calculate the Q parities for the sector.
* The 26 Q vectors of length 43 are combined with the GF8_Q_COEFFS.
*/
static void calc_Q_parity(u_int8_t *sector)
{
int i, j;
u_int16_t q01_lsb, q01_msb;
u_int8_t *q_lsb_start;
u_int8_t *q_lsb;
u_int8_t *q0, *q1, *q_start;
u_int8_t d0,d1;
q_lsb_start = sector + LEC_HEADER_OFFSET;
q_start = sector + LEC_MODE1_Q_PARITY_OFFSET;
q1 = sector + LEC_MODE1_Q_PARITY_OFFSET;
q0 = sector + LEC_MODE1_Q_PARITY_OFFSET + 2 * 26;
for (i = 0; i <= 25; i++) {
q_lsb = q_lsb_start;
q01_lsb = q01_msb = 0;
for (j = 0; j <= 42; j++) {
d0 = *q_lsb;
d1 = *(q_lsb+1);
q01_lsb ^= CF8_Q_COEFFS_RESULTS_01[j][d0];
q01_msb ^= CF8_Q_COEFFS_RESULTS_01[j][d1];
q_lsb += 2 * 44;
if (q_lsb >= q_start) {
q_lsb -= 2 * 1118;
}
}
*q0 = q01_lsb;
*(q0 + 1) = q01_msb;
*q1 = q01_lsb>>8;
*(q1 + 1) = q01_msb>>8;
q0 += 2;
q1 += 2;
q_lsb_start += 2 * 43;
}
}
/* Encodes a MODE 0 sector.
* 'adr' is the current physical sector address
* 'sector' must be 2352 byte wide
*/
void lec_encode_mode0_sector(u_int32_t adr, u_int8_t *sector)
{
u_int16_t i;
set_sync_pattern(sector);
set_sector_header(0, adr, sector);
sector += 16;
for (i = 0; i < 2336; i++)
*sector++ = 0;
}
/* Encodes a MODE 1 sector.
* 'adr' is the current physical sector address
* 'sector' must be 2352 byte wide containing 2048 bytes user data at
* offset 16
*/
void lec_encode_mode1_sector(u_int32_t adr, u_int8_t *sector)
{
set_sync_pattern(sector);
set_sector_header(1, adr, sector);
calc_mode1_edc(sector);
/* clear the intermediate field */
sector[LEC_MODE1_INTERMEDIATE_OFFSET] =
sector[LEC_MODE1_INTERMEDIATE_OFFSET + 1] =
sector[LEC_MODE1_INTERMEDIATE_OFFSET + 2] =
sector[LEC_MODE1_INTERMEDIATE_OFFSET + 3] =
sector[LEC_MODE1_INTERMEDIATE_OFFSET + 4] =
sector[LEC_MODE1_INTERMEDIATE_OFFSET + 5] =
sector[LEC_MODE1_INTERMEDIATE_OFFSET + 6] =
sector[LEC_MODE1_INTERMEDIATE_OFFSET + 7] = 0;
calc_P_parity(sector);
calc_Q_parity(sector);
}
/* Encodes a MODE 2 sector.
* 'adr' is the current physical sector address
* 'sector' must be 2352 byte wide containing 2336 bytes user data at
* offset 16
*/
void lec_encode_mode2_sector(u_int32_t adr, u_int8_t *sector)
{
set_sync_pattern(sector);
set_sector_header(2, adr, sector);
}
/* Encodes a XA form 1 sector.
* 'adr' is the current physical sector address
* 'sector' must be 2352 byte wide containing 2048+8 bytes user data at
* offset 16
*/
void lec_encode_mode2_form1_sector(u_int32_t adr, u_int8_t *sector)
{
set_sync_pattern(sector);
calc_mode2_form1_edc(sector);
/* P/Q partiy must not contain the sector header so clear it */
sector[LEC_HEADER_OFFSET] =
sector[LEC_HEADER_OFFSET + 1] =
sector[LEC_HEADER_OFFSET + 2] =
sector[LEC_HEADER_OFFSET + 3] = 0;
calc_P_parity(sector);
calc_Q_parity(sector);
/* finally add the sector header */
set_sector_header(2, adr, sector);
}
/* Encodes a XA form 2 sector.
* 'adr' is the current physical sector address
* 'sector' must be 2352 byte wide containing 2324+8 bytes user data at
* offset 16
*/
void lec_encode_mode2_form2_sector(u_int32_t adr, u_int8_t *sector)
{
set_sync_pattern(sector);
calc_mode2_form2_edc(sector);
set_sector_header(2, adr, sector);
}
/* Scrambles and byte swaps an encoded sector.
* 'sector' must be 2352 byte wide.
*/
void lec_scramble(u_int8_t *sector)
{
u_int16_t i;
const u_int8_t *stable = SCRAMBLE_TABLE;
u_int8_t *p = sector;
u_int8_t tmp;
for (i = 0; i < 6; i++) {
/* just swap bytes of sector sync */
tmp = *p;
*p = *(p + 1);
p++;
*p++ = tmp;
}
for (;i < (2352 / 2); i++) {
/* scramble and swap bytes */
tmp = *p ^ *stable++;
*p = *(p + 1) ^ *stable++;
p++;
*p++ = tmp;
}
}
#if 0
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
int main(int argc, char **argv)
{
char *infile;
char *outfile;
int fd_in, fd_out;
u_int8_t buffer1[2352];
u_int8_t buffer2[2352];
u_int32_t lba;
int i;
#if 0
for (i = 0; i < 2048; i++)
buffer1[i + 16] = 234;
lba = 150;
for (i = 0; i < 100000; i++) {
lec_encode_mode1_sector(lba, buffer1);
lec_scramble(buffer2);
lba++;
}
#else
if (argc != 3)
return 1;
infile = argv[1];
outfile = argv[2];
if ((fd_in = open(infile, O_RDONLY)) < 0) {
perror("Cannot open input file");
return 1;
}
if ((fd_out = open(outfile, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) {
perror("Cannot open output file");
return 1;
}
lba = 150;
do {
if (read(fd_in, buffer1, 2352) != 2352)
break;
switch (*(buffer1 + 12 + 3)) {
case 1:
memcpy(buffer2 + 16, buffer1 + 16, 2048);
lec_encode_mode1_sector(lba, buffer2);
break;
case 2:
if ((*(buffer1 + 12 + 4 + 2) & 0x20) != 0) {
/* form 2 sector */
memcpy(buffer2 + 16, buffer1 + 16, 2324 + 8);
lec_encode_mode2_form2_sector(lba, buffer2);
}
else {
/* form 1 sector */
memcpy(buffer2 + 16, buffer1 + 16, 2048 + 8);
lec_encode_mode2_form1_sector(lba, buffer2);
}
break;
}
if (memcmp(buffer1, buffer2, 2352) != 0) {
printf("Verify error at lba %ld\n", lba);
}
lec_scramble(buffer2);
write(fd_out, buffer2, 2352);
lba++;
} while (1);
close(fd_in);
close(fd_out);
#endif
return 0;
}
#endif

77
psx/mednadisc/cdrom/lec.h Normal file
View File

@ -0,0 +1,77 @@
/* cdrdao - write audio CD-Rs in disc-at-once mode
*
* Copyright (C) 1998-2002 Andreas Mueller <mueller@daneb.ping.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __LEC_H__
#define __LEC_H__
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <sys/types.h>
#include <inttypes.h>
typedef uint32_t u_int32_t;
typedef uint16_t u_int16_t;
typedef uint8_t u_int8_t;
#ifndef TRUE
#define TRUE 1
#endif
/* Encodes a MODE 0 sector.
* 'adr' is the current physical sector address
* 'sector' must be 2352 byte wide
*/
void lec_encode_mode0_sector(u_int32_t adr, u_int8_t *sector);
/* Encodes a MODE 1 sector.
* 'adr' is the current physical sector address
* 'sector' must be 2352 byte wide containing 2048 bytes user data at
* offset 16
*/
void lec_encode_mode1_sector(u_int32_t adr, u_int8_t *sector);
/* Encodes a MODE 2 sector.
* 'adr' is the current physical sector address
* 'sector' must be 2352 byte wide containing 2336 bytes user data at
* offset 16
*/
void lec_encode_mode2_sector(u_int32_t adr, u_int8_t *sector);
/* Encodes a XA form 1 sector.
* 'adr' is the current physical sector address
* 'sector' must be 2352 byte wide containing 2048+8 bytes user data at
* offset 16
*/
void lec_encode_mode2_form1_sector(u_int32_t adr, u_int8_t *sector);
/* Encodes a XA form 2 sector.
* 'adr' is the current physical sector address
* 'sector' must be 2352 byte wide containing 2324+8 bytes user data at
* offset 16
*/
void lec_encode_mode2_form2_sector(u_int32_t adr, u_int8_t *sector);
/* Scrambles and byte swaps an encoded sector.
* 'sector' must be 2352 byte wide.
*/
void lec_scramble(u_int8_t *sector);
#endif

View File

@ -0,0 +1,203 @@
/* dvdisaster: Additional error correction for optical media.
* Copyright (C) 2004-2007 Carsten Gnoerlich.
* Project home page: http://www.dvdisaster.com
* Email: carsten@dvdisaster.com -or- cgnoerlich@fsfe.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA,
* or direct your browser at http://www.gnu.org.
*/
#include "dvdisaster.h"
static GaloisTables *gt = NULL; /* for L-EC Reed-Solomon */
static ReedSolomonTables *rt = NULL;
bool Init_LEC_Correct(void)
{
gt = CreateGaloisTables(0x11d);
rt = CreateReedSolomonTables(gt, 0, 1, 10);
return(1);
}
void Kill_LEC_Correct(void)
{
FreeGaloisTables(gt);
FreeReedSolomonTables(rt);
}
/***
*** CD level CRC calculation
***/
/*
* Test raw sector against its 32bit CRC.
* Returns TRUE if frame is good.
*/
int CheckEDC(const unsigned char *cd_frame, bool xa_mode)
{
unsigned int expected_crc, real_crc;
unsigned int crc_base = xa_mode ? 2072 : 2064;
expected_crc = cd_frame[crc_base + 0] << 0;
expected_crc |= cd_frame[crc_base + 1] << 8;
expected_crc |= cd_frame[crc_base + 2] << 16;
expected_crc |= cd_frame[crc_base + 3] << 24;
if(xa_mode)
real_crc = EDCCrc32(cd_frame+16, 2056);
else
real_crc = EDCCrc32(cd_frame, 2064);
if(expected_crc == real_crc)
return(1);
else
{
//printf("Bad EDC CRC: Calculated: %08x, Recorded: %08x\n", real_crc, expected_crc);
return(0);
}
}
/***
*** A very simple L-EC error correction.
***
* Perform just one pass over the Q and P vectors to see if everything
* is okay respectively correct minor errors. This is pretty much the
* same stuff the drive is supposed to do in the final L-EC stage.
*/
static int simple_lec(unsigned char *frame)
{
unsigned char byte_state[2352];
unsigned char p_vector[P_VECTOR_SIZE];
unsigned char q_vector[Q_VECTOR_SIZE];
unsigned char p_state[P_VECTOR_SIZE];
int erasures[Q_VECTOR_SIZE], erasure_count;
int ignore[2];
int p_failures, q_failures;
int p_corrected, q_corrected;
int p,q;
/* Setup */
memset(byte_state, 0, 2352);
p_failures = q_failures = 0;
p_corrected = q_corrected = 0;
/* Perform Q-Parity error correction */
for(q=0; q<N_Q_VECTORS; q++)
{ int err;
/* We have no erasure information for Q vectors */
GetQVector(frame, q_vector, q);
err = DecodePQ(rt, q_vector, Q_PADDING, ignore, 0);
/* See what we've got */
if(err < 0) /* Uncorrectable. Mark bytes are erasure. */
{ q_failures++;
FillQVector(byte_state, 1, q);
}
else /* Correctable */
{ if(err == 1 || err == 2) /* Store back corrected vector */
{ SetQVector(frame, q_vector, q);
q_corrected++;
}
}
}
/* Perform P-Parity error correction */
for(p=0; p<N_P_VECTORS; p++)
{ int err,i;
/* Try error correction without erasure information */
GetPVector(frame, p_vector, p);
err = DecodePQ(rt, p_vector, P_PADDING, ignore, 0);
/* If unsuccessful, try again using erasures.
Erasure information is uncertain, so try this last. */
if(err < 0 || err > 2)
{ GetPVector(byte_state, p_state, p);
erasure_count = 0;
for(i=0; i<P_VECTOR_SIZE; i++)
if(p_state[i])
erasures[erasure_count++] = i;
if(erasure_count > 0 && erasure_count <= 2)
{ GetPVector(frame, p_vector, p);
err = DecodePQ(rt, p_vector, P_PADDING, erasures, erasure_count);
}
}
/* See what we've got */
if(err < 0) /* Uncorrectable. */
{ p_failures++;
}
else /* Correctable. */
{ if(err == 1 || err == 2) /* Store back corrected vector */
{ SetPVector(frame, p_vector, p);
p_corrected++;
}
}
}
/* Sum up */
if(q_failures || p_failures || q_corrected || p_corrected)
{
return 1;
}
return 0;
}
/***
*** Validate CD raw sector
***/
int ValidateRawSector(unsigned char *frame, bool xaMode)
{
int lec_did_sth = FALSE_0;
/* Do simple L-EC.
It seems that drives stop their internal L-EC as soon as the
EDC is okay, so we may see uncorrected errors in the parity bytes.
Since we are also interested in the user data only and doing the
L-EC is expensive, we skip our L-EC as well when the EDC is fine. */
if(!CheckEDC(frame, xaMode))
{
lec_did_sth = simple_lec(frame);
}
/* Test internal sector checksum again */
if(!CheckEDC(frame, xaMode))
{
/* EDC failure in RAW sector */
return FALSE_0;
}
return TRUE_1;
}

View File

@ -0,0 +1,259 @@
/********************************************************
* *
* PC Engine CD Command 0xD8 - SAPSP *
* *
********************************************************/
static void DoNEC_PCE_SAPSP(const uint8 *cdb)
{
uint32 new_read_sec_start;
//printf("Set audio start: %02x %02x %02x %02x %02x %02x %02x\n", cdb[9], cdb[1], cdb[2], cdb[3], cdb[4], cdb[5], cdb[6]);
switch (cdb[9] & 0xc0)
{
default: SCSIDBG("Unknown SAPSP 9: %02x\n", cdb[9]);
case 0x00:
new_read_sec_start = (cdb[3] << 16) | (cdb[4] << 8) | cdb[5];
break;
case 0x40:
new_read_sec_start = AMSF_to_LBA(BCD_to_U8(cdb[2]), BCD_to_U8(cdb[3]), BCD_to_U8(cdb[4]));
break;
case 0x80:
{
int track = BCD_to_U8(cdb[2]);
if(!track)
track = 1;
else if(track >= toc.last_track + 1)
track = 100;
new_read_sec_start = toc.tracks[track].lba;
}
break;
}
//printf("%lld\n", (long long)(monotonic_timestamp - pce_lastsapsp_timestamp) * 1000 / System_Clock);
if(cdda.CDDAStatus == CDDASTATUS_PLAYING && new_read_sec_start == read_sec_start && ((int64)(monotonic_timestamp - pce_lastsapsp_timestamp) * 1000 / System_Clock) < 190)
{
pce_lastsapsp_timestamp = monotonic_timestamp;
SendStatusAndMessage(STATUS_GOOD, 0x00);
CDIRQCallback(SCSICD_IRQ_DATA_TRANSFER_DONE);
return;
}
pce_lastsapsp_timestamp = monotonic_timestamp;
read_sec = read_sec_start = new_read_sec_start;
read_sec_end = toc.tracks[100].lba;
cdda.CDDAReadPos = 588;
cdda.CDDAStatus = CDDASTATUS_PAUSED;
cdda.PlayMode = PLAYMODE_SILENT;
if(cdb[1])
{
cdda.PlayMode = PLAYMODE_NORMAL;
cdda.CDDAStatus = CDDASTATUS_PLAYING;
}
if(read_sec < toc.tracks[100].lba)
Cur_CDIF->HintReadSector(read_sec);
SendStatusAndMessage(STATUS_GOOD, 0x00);
CDIRQCallback(SCSICD_IRQ_DATA_TRANSFER_DONE);
}
/********************************************************
* *
* PC Engine CD Command 0xD9 - SAPEP *
* *
********************************************************/
static void DoNEC_PCE_SAPEP(const uint8 *cdb)
{
uint32 new_read_sec_end;
//printf("Set audio end: %02x %02x %02x %02x %02x %02x %02x\n", cdb[9], cdb[1], cdb[2], cdb[3], cdb[4], cdb[5], cdb[6]);
switch (cdb[9] & 0xc0)
{
default: SCSIDBG("Unknown SAPEP 9: %02x\n", cdb[9]);
case 0x00:
new_read_sec_end = (cdb[3] << 16) | (cdb[4] << 8) | cdb[5];
break;
case 0x40:
new_read_sec_end = BCD_to_U8(cdb[4]) + 75 * (BCD_to_U8(cdb[3]) + 60 * BCD_to_U8(cdb[2]));
new_read_sec_end -= 150;
break;
case 0x80:
{
int track = BCD_to_U8(cdb[2]);
if(!track)
track = 1;
else if(track >= toc.last_track + 1)
track = 100;
new_read_sec_end = toc.tracks[track].lba;
}
break;
}
read_sec_end = new_read_sec_end;
switch(cdb[1]) // PCE CD(TODO: Confirm these, and check the mode mask):
{
default:
case 0x03: cdda.PlayMode = PLAYMODE_NORMAL;
cdda.CDDAStatus = CDDASTATUS_PLAYING;
break;
case 0x02: cdda.PlayMode = PLAYMODE_INTERRUPT;
cdda.CDDAStatus = CDDASTATUS_PLAYING;
break;
case 0x01: cdda.PlayMode = PLAYMODE_LOOP;
cdda.CDDAStatus = CDDASTATUS_PLAYING;
break;
case 0x00: cdda.PlayMode = PLAYMODE_SILENT;
cdda.CDDAStatus = CDDASTATUS_STOPPED;
break;
}
SendStatusAndMessage(STATUS_GOOD, 0x00);
}
/********************************************************
* *
* PC Engine CD Command 0xDA - Pause *
* *
********************************************************/
static void DoNEC_PCE_PAUSE(const uint8 *cdb)
{
if(cdda.CDDAStatus != CDDASTATUS_STOPPED) // Hmm, should we give an error if it tries to pause and it's already paused?
{
cdda.CDDAStatus = CDDASTATUS_PAUSED;
SendStatusAndMessage(STATUS_GOOD, 0x00);
}
else // Definitely give an error if it tries to pause when no track is playing!
{
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_AUDIO_NOT_PLAYING);
}
}
/********************************************************
* *
* PC Engine CD Command 0xDD - Read Subchannel Q *
* *
********************************************************/
static void DoNEC_PCE_READSUBQ(const uint8 *cdb)
{
uint8 *SubQBuf = cd.SubQBuf[QMode_Time];
uint8 data_in[8192];
memset(data_in, 0x00, 10);
data_in[2] = SubQBuf[1]; // Track
data_in[3] = SubQBuf[2]; // Index
data_in[4] = SubQBuf[3]; // M(rel)
data_in[5] = SubQBuf[4]; // S(rel)
data_in[6] = SubQBuf[5]; // F(rel)
data_in[7] = SubQBuf[7]; // M(abs)
data_in[8] = SubQBuf[8]; // S(abs)
data_in[9] = SubQBuf[9]; // F(abs)
if(cdda.CDDAStatus == CDDASTATUS_PAUSED)
data_in[0] = 2; // Pause
else if(cdda.CDDAStatus == CDDASTATUS_PLAYING || cdda.CDDAStatus == CDDASTATUS_SCANNING) // FIXME: Is this the correct status code for scanning playback?
data_in[0] = 0; // Playing
else
data_in[0] = 3; // Stopped
DoSimpleDataIn(data_in, 10);
}
/********************************************************
* *
* PC Engine CD Command 0xDE - Get Directory Info *
* *
********************************************************/
static void DoNEC_PCE_GETDIRINFO(const uint8 *cdb)
{
// Problems:
// Returned data lengths on real PCE are not confirmed.
// Mode 0x03 behavior not tested on real PCE
uint8 data_in[2048];
uint32 data_in_size = 0;
memset(data_in, 0, sizeof(data_in));
switch(cdb[1])
{
default: MDFN_DispMessage("Unknown GETDIRINFO Mode: %02x", cdb[1]);
printf("Unknown GETDIRINFO Mode: %02x", cdb[1]);
case 0x0:
data_in[0] = U8_to_BCD(toc.first_track);
data_in[1] = U8_to_BCD(toc.last_track);
data_in_size = 2;
break;
case 0x1:
{
uint8 m, s, f;
LBA_to_AMSF(toc.tracks[100].lba, &m, &s, &f);
data_in[0] = U8_to_BCD(m);
data_in[1] = U8_to_BCD(s);
data_in[2] = U8_to_BCD(f);
data_in_size = 3;
}
break;
case 0x2:
{
uint8 m, s, f;
int track = BCD_to_U8(cdb[2]);
if(!track)
track = 1;
else if(cdb[2] == 0xAA)
{
track = 100;
}
else if(track > 99)
{
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
return;
}
LBA_to_AMSF(toc.tracks[track].lba, &m, &s, &f);
data_in[0] = U8_to_BCD(m);
data_in[1] = U8_to_BCD(s);
data_in[2] = U8_to_BCD(f);
data_in[3] = toc.tracks[track].control;
data_in_size = 4;
}
break;
}
DoSimpleDataIn(data_in, data_in_size);
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,99 @@
#ifndef __PCFX_SCSICD_H
#define __PCFX_SCSICD_H
typedef int32 scsicd_timestamp_t;
typedef struct
{
// Data bus(FIXME: we should have a variable for the target and the initiator, and OR them together to be truly accurate).
uint8 DB;
uint32 signals;
// Signals under our(the "target") control.
//bool BSY, MSG, CD, REQ, IO;
// Signals under the control of the initiator(not us!)
//bool kingACK, kingRST, kingSEL, kingATN;
} scsicd_bus_t;
extern scsicd_bus_t cd_bus; // Don't access this structure directly by name outside of scsicd.c, but use the macros below.
// Signals under our(the "target") control.
#define SCSICD_IO_mask 0x001
#define SCSICD_CD_mask 0x002
#define SCSICD_MSG_mask 0x004
#define SCSICD_REQ_mask 0x008
#define SCSICD_BSY_mask 0x010
// Signals under the control of the initiator(not us!)
#define SCSICD_kingRST_mask 0x020
#define SCSICD_kingACK_mask 0x040
#define SCSICD_kingATN_mask 0x080
#define SCSICD_kingSEL_mask 0x100
#define BSY_signal ((const bool)(cd_bus.signals & SCSICD_BSY_mask))
#define ACK_signal ((const bool)(cd_bus.signals & SCSICD_kingACK_mask))
#define RST_signal ((const bool)(cd_bus.signals & SCSICD_kingRST_mask))
#define MSG_signal ((const bool)(cd_bus.signals & SCSICD_MSG_mask))
#define SEL_signal ((const bool)(cd_bus.signals & SCSICD_kingSEL_mask))
#define REQ_signal ((const bool)(cd_bus.signals & SCSICD_REQ_mask))
#define IO_signal ((const bool)(cd_bus.signals & SCSICD_IO_mask))
#define CD_signal ((const bool)(cd_bus.signals & SCSICD_CD_mask))
#define ATN_signal ((const bool)(cd_bus.signals & SCSICD_kingATN_mask))
#define DB_signal ((const uint8)cd_bus.DB)
#define SCSICD_GetDB() DB_signal
#define SCSICD_GetBSY() BSY_signal
#define SCSICD_GetIO() IO_signal
#define SCSICD_GetCD() CD_signal
#define SCSICD_GetMSG() MSG_signal
#define SCSICD_GetREQ() REQ_signal
// Should we phase out getting these initiator-driven signals like this(the initiator really should keep track of them itself)?
#define SCSICD_GetACK() ACK_signal
#define SCSICD_GetRST() RST_signal
#define SCSICD_GetSEL() SEL_signal
#define SCSICD_GetATN() ATN_signal
void SCSICD_Power(scsicd_timestamp_t system_timestamp);
void SCSICD_SetDB(uint8 data);
// These SCSICD_Set* functions are kind of misnomers, at least in comparison to the SCSICD_Get* functions...
// They will set/clear the bits corresponding to the KING's side of the bus.
void SCSICD_SetACK(bool set);
void SCSICD_SetSEL(bool set);
void SCSICD_SetRST(bool set);
void SCSICD_SetATN(bool set);
uint32 SCSICD_Run(scsicd_timestamp_t);
void SCSICD_ResetTS(uint32 ts_base);
enum
{
SCSICD_PCE = 1,
SCSICD_PCFX
};
enum
{
SCSICD_IRQ_DATA_TRANSFER_DONE = 1,
SCSICD_IRQ_DATA_TRANSFER_READY,
SCSICD_IRQ_MAGICAL_REQ,
};
void SCSICD_GetCDDAValues(int16 &left, int16 &right);
void SCSICD_SetLog(void (*logfunc)(const char *, const char *, ...));
void SCSICD_Init(int type, int CDDATimeDiv, int32* left_hrbuf, int32* right_hrbuf, uint32 TransferRate, uint32 SystemClock, void (*IRQFunc)(int), void (*SSCFunc)(uint8, int));
void SCSICD_Close(void);
void SCSICD_SetTransferRate(uint32 TransferRate);
void SCSICD_SetCDDAVolume(double left, double right);
void SCSICD_StateAction(StateMem *sm, const unsigned load, const bool data_only, const char *sname);
void SCSICD_SetDisc(bool tray_open, CDIF *cdif, bool no_emu_side_effects = false);
#endif

View File

@ -0,0 +1,69 @@
// WARNING: Check resampling algorithm in scsicd.cpp for overflows if any value in here is negative.
/* -1 */ { 1777, 12211, 27812, 27640, 11965, 1703, 9, 0 }, // 83117 83119.332059(diff = 2.332059)
/* 0 */ { 1702, 11965, 27640, 27811, 12211, 1777, 11, 0 }, // 83117 83121.547903(diff = 4.547903)
/* 1 */ { 1630, 11720, 27463, 27977, 12459, 1854, 14, 0 }, // 83117 83123.444392(diff = 6.444392)
/* 2 */ { 1560, 11478, 27282, 28139, 12708, 1933, 17, 0 }, // 83117 83125.036510(diff = 8.036510)
/* 3 */ { 1492, 11238, 27098, 28296, 12959, 2014, 20, 0 }, // 83117 83126.338722(diff = 9.338722)
/* 4 */ { 1427, 11000, 26909, 28448, 13212, 2098, 23, 0 }, // 83117 83127.364983(diff = 10.364983)
/* 5 */ { 1363, 10764, 26716, 28595, 13467, 2185, 27, 0 }, // 83117 83128.128743(diff = 11.128743)
/* 6 */ { 1302, 10530, 26519, 28738, 13723, 2274, 31, 0 }, // 83117 83128.642956(diff = 11.642956)
/* 7 */ { 1242, 10299, 26319, 28876, 13981, 2365, 35, 0 }, // 83117 83128.920096(diff = 11.920096)
/* 8 */ { 1185, 10071, 26115, 29009, 14239, 2459, 39, 0 }, // 83117 83128.972128(diff = 11.972128)
/* 9 */ { 1129, 9844, 25907, 29137, 14499, 2556, 45, 0 }, // 83117 83128.810568(diff = 11.810568)
/* 10 */ { 1076, 9620, 25695, 29260, 14761, 2655, 50, 0 }, // 83117 83128.446456(diff = 11.446456)
/* 11 */ { 1024, 9399, 25481, 29377, 15023, 2757, 56, 0 }, // 83117 83127.890369(diff = 10.890369)
/* 12 */ { 975, 9180, 25263, 29489, 15287, 2861, 62, 0 }, // 83117 83127.152431(diff = 10.152431)
/* 13 */ { 927, 8964, 25041, 29596, 15552, 2968, 69, 0 }, // 83117 83126.242312(diff = 9.242312)
/* 14 */ { 880, 8750, 24817, 29698, 15818, 3078, 76, 0 }, // 83117 83125.169251(diff = 8.169251)
/* 15 */ { 836, 8539, 24590, 29794, 16083, 3191, 84, 0 }, // 83117 83123.942037(diff = 6.942037)
/* 16 */ { 793, 8331, 24359, 29884, 16350, 3307, 93, 0 }, // 83117 83122.569034(diff = 5.569034)
/* 17 */ { 752, 8125, 24126, 29969, 16618, 3425, 102, 0 }, // 83117 83121.058175(diff = 4.058175)
/* 18 */ { 712, 7923, 23890, 30049, 16886, 3546, 111, 0 }, // 83117 83119.416975(diff = 2.416975)
/* 19 */ { 674, 7723, 23651, 30123, 17154, 3670, 122, 0 }, // 83117 83117.652622(diff = 0.652622)
/* 20 */ { 638, 7526, 23410, 30191, 17422, 3797, 133, 0 }, // 83117 83115.771622(diff = 1.228378)
/* 21 */ { 603, 7331, 23167, 30254, 17691, 3927, 144, 0 }, // 83117 83113.780335(diff = 3.219665)
/* 22 */ { 569, 7140, 22922, 30310, 17960, 4059, 157, 0 }, // 83117 83111.684630(diff = 5.315370)
/* 23 */ { 537, 6951, 22674, 30361, 18229, 4195, 170, 0 }, // 83117 83109.489972(diff = 7.510028)
/* 24 */ { 506, 6766, 22424, 30407, 18497, 4334, 183, 0 }, // 83117 83107.201429(diff = 9.798571)
/* 25 */ { 477, 6583, 22172, 30446, 18766, 4475, 198, 0 }, // 83117 83104.823668(diff = 12.176332)
/* 26 */ { 449, 6403, 21919, 30479, 19034, 4619, 214, 0 }, // 83117 83102.360963(diff = 14.639037)
/* 27 */ { 422, 6226, 21664, 30507, 19301, 4767, 230, 0 }, // 83117 83099.817193(diff = 17.182807)
/* 28 */ { 396, 6053, 21407, 30529, 19568, 4917, 247, 0 }, // 83117 83097.195820(diff = 19.804180)
/* 29 */ { 372, 5882, 21148, 30545, 19834, 5071, 265, 0 }, // 83117 83094.499993(diff = 22.500007)
/* 30 */ { 348, 5714, 20888, 30555, 20100, 5227, 285, 0 }, // 83117 83091.732389(diff = 25.267611)
/* 31 */ { 326, 5549, 20627, 30559, 20365, 5386, 305, 0 }, // 83117 83088.895321(diff = 28.104679)
/* 32 */ { 305, 5386, 20365, 30559, 20627, 5549, 326, 0 }, // 83117 83088.895321(diff = 28.104679)
/* 33 */ { 285, 5227, 20100, 30555, 20888, 5714, 348, 0 }, // 83117 83091.732389(diff = 25.267611)
/* 34 */ { 265, 5071, 19834, 30545, 21148, 5882, 372, 0 }, // 83117 83094.499993(diff = 22.500007)
/* 35 */ { 247, 4917, 19568, 30529, 21407, 6053, 396, 0 }, // 83117 83097.195820(diff = 19.804180)
/* 36 */ { 230, 4767, 19301, 30507, 21664, 6226, 422, 0 }, // 83117 83099.817193(diff = 17.182807)
/* 37 */ { 214, 4619, 19034, 30479, 21919, 6403, 449, 0 }, // 83117 83102.360963(diff = 14.639037)
/* 38 */ { 198, 4475, 18766, 30446, 22172, 6583, 477, 0 }, // 83117 83104.823668(diff = 12.176332)
/* 39 */ { 183, 4334, 18497, 30407, 22424, 6766, 506, 0 }, // 83117 83107.201429(diff = 9.798571)
/* 40 */ { 170, 4195, 18229, 30361, 22674, 6951, 537, 0 }, // 83117 83109.489972(diff = 7.510028)
/* 41 */ { 157, 4059, 17960, 30310, 22922, 7140, 569, 0 }, // 83117 83111.684630(diff = 5.315370)
/* 42 */ { 144, 3927, 17691, 30254, 23167, 7331, 603, 0 }, // 83117 83113.780335(diff = 3.219665)
/* 43 */ { 133, 3797, 17422, 30191, 23410, 7526, 638, 0 }, // 83117 83115.771622(diff = 1.228378)
/* 44 */ { 122, 3670, 17154, 30123, 23651, 7723, 674, 0 }, // 83117 83117.652622(diff = 0.652622)
/* 45 */ { 111, 3546, 16886, 30049, 23890, 7923, 712, 0 }, // 83117 83119.416975(diff = 2.416975)
/* 46 */ { 102, 3425, 16618, 29969, 24126, 8125, 752, 0 }, // 83117 83121.058175(diff = 4.058175)
/* 47 */ { 93, 3307, 16350, 29884, 24359, 8331, 793, 0 }, // 83117 83122.569034(diff = 5.569034)
/* 48 */ { 84, 3191, 16083, 29794, 24590, 8539, 836, 0 }, // 83117 83123.942037(diff = 6.942037)
/* 49 */ { 76, 3078, 15818, 29698, 24817, 8750, 880, 0 }, // 83117 83125.169251(diff = 8.169251)
/* 50 */ { 69, 2968, 15552, 29596, 25041, 8964, 927, 0 }, // 83117 83126.242312(diff = 9.242312)
/* 51 */ { 62, 2861, 15287, 29489, 25263, 9180, 975, 0 }, // 83117 83127.152431(diff = 10.152431)
/* 52 */ { 56, 2757, 15023, 29377, 25481, 9399, 1024, 0 }, // 83117 83127.890369(diff = 10.890369)
/* 53 */ { 50, 2655, 14761, 29260, 25695, 9620, 1076, 0 }, // 83117 83128.446456(diff = 11.446456)
/* 54 */ { 45, 2556, 14499, 29137, 25907, 9844, 1129, 0 }, // 83117 83128.810568(diff = 11.810568)
/* 55 */ { 39, 2459, 14239, 29009, 26115, 10071, 1185, 0 }, // 83117 83128.972128(diff = 11.972128)
/* 56 */ { 35, 2365, 13981, 28876, 26319, 10299, 1242, 0 }, // 83117 83128.920096(diff = 11.920096)
/* 57 */ { 31, 2274, 13723, 28738, 26519, 10530, 1302, 0 }, // 83117 83128.642956(diff = 11.642956)
/* 58 */ { 27, 2185, 13467, 28595, 26716, 10764, 1363, 0 }, // 83117 83128.128743(diff = 11.128743)
/* 59 */ { 23, 2098, 13212, 28448, 26909, 11000, 1427, 0 }, // 83117 83127.364983(diff = 10.364983)
/* 60 */ { 20, 2014, 12959, 28296, 27098, 11238, 1492, 0 }, // 83117 83126.338722(diff = 9.338722)
/* 61 */ { 17, 1933, 12708, 28139, 27282, 11478, 1560, 0 }, // 83117 83125.036510(diff = 8.036510)
/* 62 */ { 14, 1854, 12459, 27977, 27463, 11720, 1630, 0 }, // 83117 83123.444392(diff = 6.444392)
/* 63 */ { 11, 1777, 12211, 27811, 27640, 11965, 1702, 0 }, // 83117 83121.547903(diff = 4.547903)
/* 64 */ { 9, 1703, 11965, 27640, 27812, 12211, 1777, 0 }, // 83117 83119.332059(diff = 2.332059)

View File

@ -0,0 +1,96 @@
#include "EW_state.h"
#include <cstring>
#include <algorithm>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
namespace EW {
NewStateDummy::NewStateDummy()
:length(0)
{
}
void NewStateDummy::Save(const void *ptr, size_t size, const char *name)
{
length += size;
}
void NewStateDummy::Load(void *ptr, size_t size, const char *name)
{
}
NewStateExternalBuffer::NewStateExternalBuffer(char *buffer, long maxlength)
:buffer(buffer), length(0), maxlength(maxlength)
{
}
void NewStateExternalBuffer::Save(const void *ptr, size_t size, const char *name)
{
if (maxlength - length >= (long)size)
{
std::memcpy(buffer + length, ptr, size);
}
length += size;
}
void NewStateExternalBuffer::Load(void *ptr, size_t size, const char *name)
{
char *dst = static_cast<char *>(ptr);
if (maxlength - length >= (long)size)
{
std::memcpy(dst, buffer + length, size);
}
length += size;
}
NewStateExternalFunctions::NewStateExternalFunctions(const FPtrs *ff)
:Save_(ff->Save_),
Load_(ff->Load_),
EnterSection_(ff->EnterSection_),
ExitSection_(ff->ExitSection_)
{
}
void NewStateExternalFunctions::Save(const void *ptr, size_t size, const char *name)
{
Save_(ptr, size, name);
}
void NewStateExternalFunctions::Load(void *ptr, size_t size, const char *name)
{
Load_(ptr, size, name);
}
void NewStateExternalFunctions::EnterSection(const char *name, ...)
{
//analysis: multiple passes to generate string not ideal, but there arent many sections.. so it should be OK. improvement would be special vararg overload
va_list ap;
va_start(ap,name);
char easybuf[32];
int size = vsnprintf(easybuf,0,name,ap);
char *ptr = easybuf;
if(size>31)
ptr = (char*)malloc(size+1);
vsprintf(ptr,name,ap);
EnterSection_(ptr);
if(ptr != easybuf)
free(ptr);
va_end(ap);
}
void NewStateExternalFunctions::ExitSection(const char *name, ...)
{
va_list ap;
va_start(ap,name);
char easybuf[32];
int size = vsnprintf(easybuf,0,name,ap);
char *ptr = easybuf;
if(size>31)
ptr = (char*)malloc(size+1);
vsprintf(ptr,name,ap);
ExitSection_(ptr);
if(ptr != easybuf)
free(ptr);
va_end(ap);
}
}

View File

@ -0,0 +1,105 @@
#ifndef NEWSTATE_H
#define NEWSTATE_H
#include <cstring>
#include <cstddef>
namespace EW
{
class NewState
{
public:
virtual void Save(const void *ptr, size_t size, const char *name) = 0;
virtual void Load(void *ptr, size_t size, const char *name) = 0;
virtual void EnterSection(const char *name, ...) { }
virtual void ExitSection(const char *name, ...) { }
};
class NewStateDummy : public NewState
{
private:
long length;
public:
NewStateDummy();
long GetLength() { return length; }
void Rewind() { length = 0; }
virtual void Save(const void *ptr, size_t size, const char *name);
virtual void Load(void *ptr, size_t size, const char *name);
};
class NewStateExternalBuffer : public NewState
{
private:
char *const buffer;
long length;
const long maxlength;
public:
NewStateExternalBuffer(char *buffer, long maxlength);
long GetLength() { return length; }
void Rewind() { length = 0; }
bool Overflow() { return length > maxlength; }
virtual void Save(const void *ptr, size_t size, const char *name);
virtual void Load(void *ptr, size_t size, const char *name);
};
struct FPtrs
{
void (*Save_)(const void *ptr, size_t size, const char *name);
void (*Load_)(void *ptr, size_t size, const char *name);
void (*EnterSection_)(const char *name);
void (*ExitSection_)(const char *name);
};
class NewStateExternalFunctions : public NewState
{
private:
void (*Save_)(const void *ptr, size_t size, const char *name);
void (*Load_)(void *ptr, size_t size, const char *name);
void (*EnterSection_)(const char *name);
void (*ExitSection_)(const char *name);
public:
NewStateExternalFunctions(const FPtrs *ff);
virtual void Save(const void *ptr, size_t size, const char *name);
virtual void Load(void *ptr, size_t size, const char *name);
virtual void EnterSection(const char *name, ...);
virtual void ExitSection(const char *name, ...);
};
// defines and explicitly instantiates
#define SYNCFUNC(x)\
template void x::SyncState<false>(EW::NewState *ns);\
template void x::SyncState<true>(EW::NewState *ns);\
template<bool isReader>void x::SyncState(EW::NewState *ns)
// N = normal variable
// P = pointer to fixed size data
// S = "sub object"
// T = "ptr to sub object"
// R = pointer, store its offset from some other pointer
// E = general purpose cased value "enum"
// first line is default value in converted enum; last line is default value in argument x
#define EBS(x,d) do { int _ttmp = (d); if (isReader) ns->Load(&_ttmp, sizeof(_ttmp), #x); if (0)
#define EVS(x,v,n) else if (!isReader && (x) == (v)) _ttmp = (n); else if (isReader && _ttmp == (n)) (x) = (v)
#define EES(x,d) else if (isReader) (x) = (d); if (!isReader) ns->Save(&_ttmp, sizeof(_ttmp), #x); } while (0)
#define RSS(x,b) do { if (isReader)\
{ ptrdiff_t _ttmp; ns->Load(&_ttmp, sizeof(_ttmp), #x); (x) = (_ttmp == (ptrdiff_t)0xdeadbeef ? 0 : (b) + _ttmp); }\
else\
{ ptrdiff_t _ttmp = (x) == 0 ? 0xdeadbeef : (x) - (b); ns->Save(&_ttmp, sizeof(_ttmp), #x); } } while (0)
#define PSS(x,s) do { if (isReader) ns->Load((x), (s), #x); else ns->Save((x), (s), #x); } while (0)
#define NSS(x) do { if (isReader) ns->Load(&(x), sizeof(x), #x); else ns->Save(&(x), sizeof(x), #x); } while (0)
#define SSS(x) do { ns->EnterSection(#x); (x).SyncState<isReader>(ns); ns->ExitSection(#x); } while (0)
#define TSS(x) do { ns->EnterSection(#x); (x)->SyncState<isReader>(ns); ns->ExitSection(#x); } while (0)
}
#endif //NEWSTATE_H

View File

@ -0,0 +1,12 @@
#ifndef __GNUC__
#pragma pack(push, 1)
#pragma warning(disable : 4103)
#endif
#ifndef __PACKED
#ifdef __GNUC__
#define __PACKED __attribute__((__packed__))
#else
#define __PACKED
#endif
#endif

View File

@ -0,0 +1,3 @@
#ifndef __GNUC__
#pragma pack(pop)
#endif

View File

@ -0,0 +1,3 @@
#include "emuware.h"
//this file intentionally empty

View File

@ -0,0 +1,160 @@
#pragma once
#include <inttypes.h>
#include <stdint.h>
#include <cstdlib>
#ifdef _MSC_VER
typedef __int64 s64;
typedef __int32 s32;
typedef __int16 s16;
typedef __int8 s8;
typedef unsigned __int64 u64;
typedef unsigned __int32 u32;
typedef unsigned __int16 u16;
typedef unsigned __int8 u8;
typedef __int64 int64;
typedef __int32 int32;
typedef __int16 int16;
typedef __int8 int8;
typedef unsigned __int64 uint64;
typedef unsigned __int32 uint32;
typedef unsigned __int16 uint16;
typedef unsigned __int8 uint8;
#else
typedef __int64_t s64;
typedef __int32_t s32;
typedef __int16_t s16;
typedef __int8_t s8;
typedef __uint64_t u64;
typedef __uint32_t u32;
typedef __uint16_t u16;
typedef __uint8_t u8;
typedef __int64_t int64;
typedef __int32_t int32;
typedef __int16_t int16;
typedef __int8_t int8;
typedef __uint64_t uint64;
typedef __uint32_t uint32;
typedef __uint16_t uint16;
typedef __uint8_t uint8;
#endif
#define final
#define noexcept
#ifdef _MSC_VER
#include <intrin.h>
//http://stackoverflow.com/questions/355967/how-to-use-msvc-intrinsics-to-get-the-equivalent-of-this-gcc-code
//if needed
//uint32_t __inline ctz( uint32_t value )
//{
// DWORD trailing_zero = 0;
//
// if ( _BitScanForward( &trailing_zero, value ) )
// {
// return trailing_zero;
// }
// else
// {
// // This is undefined, I better choose 32 than 0
// return 32;
// }
//}
uint32 __inline __builtin_clz( uint32_t value )
{
unsigned long leading_zero = 0;
if ( _BitScanReverse( &leading_zero, value ) )
{
return 31 - leading_zero;
}
else
{
// Same remarks as above
return 32;
}
}
#endif
//#if MDFN_GCC_VERSION >= MDFN_MAKE_GCCV(4,7,0)
// #define MDFN_ASSUME_ALIGNED(p, align) __builtin_assume_aligned((p), (align))
//#else
// #define MDFN_ASSUME_ALIGNED(p, align) (p)
//#endif
#define MDFN_ASSUME_ALIGNED(p, align) (p)
//#define MDFN_WARN_UNUSED_RESULT __attribute__ ((warn_unused_result))
#define MDFN_WARN_UNUSED_RESULT
//#define MDFN_COLD __attribute__((cold))
#define MDFN_COLD
//#define NO_INLINE __attribute__((noinline))
#define NO_INLINE
//#define MDFN_UNLIKELY(n) __builtin_expect((n) != 0, 0)
//#define MDFN_LIKELY(n) __builtin_expect((n) != 0, 1)
#define MDFN_UNLIKELY(n) (n)
#define MDFN_LIKELY(n) (n)
//#define MDFN_NOWARN_UNUSED __attribute__((unused))
#define MDFN_NOWARN_UNUSED
//#define MDFN_FORMATSTR(a,b,c) __attribute__ ((format (a, b, c)))
#define MDFN_FORMATSTR(a,b,c)
#define INLINE inline
#ifndef UNALIGNED
#define UNALIGNED
#endif
#ifdef _MSC_VER
#define snprintf _snprintf
#define vsnprintf _vsnprintf
#define strcasecmp _stricmp
#define strncasecmp _strnicmp
#endif
#define TRUE_1 1
#define FALSE_0 0
#ifndef ARRAY_SIZE
//taken from winnt.h
extern "C++" // templates cannot be declared to have 'C' linkage
template <typename T, size_t N>
char (*BLAHBLAHBLAH( UNALIGNED T (&)[N] ))[N];
#define ARRAY_SIZE(A) (sizeof(*BLAHBLAHBLAH(A)))
#endif
//------------alignment macros-------------
//dont apply these to types without further testing. it only works portably here on declarations of variables
//cant we find a pattern other people use more successfully?
#if defined(_MSC_VER) || defined(__INTEL_COMPILER)
#define EW_VAR_ALIGN(X) __declspec(align(X))
#elif defined(__GNUC__)
#define EW_VAR_ALIGN(X) __attribute__ ((aligned (X)))
#else
#error
#endif
//---------------------------------------------
#ifdef EW_EXPORT
#undef EW_EXPORT
#define EW_EXPORT extern "C" __declspec(dllexport)
#else
#define EW_EXPORT extern "C" __declspec(dllimport)
#endif
#define SIZEOF_DOUBLE 8
#define LSB_FIRST
//no MSVC support, no use anyway??
#define override

View File

@ -0,0 +1,138 @@
------------------------------------------------------------------------
r26 | 2009-10-02 13:36:47 +0400 | 2 lines
[Issue 5] Change <stdint.h> to "stdint.h" to let compiler search for it in local directory.
------------------------------------------------------------------------
r25 | 2009-09-17 23:46:49 +0400 | 2 lines
[Issue 4] Fix incorrect int8_t behaviour if compiled with /J flag.
------------------------------------------------------------------------
r24 | 2009-05-13 14:53:48 +0400 | 2 lines
Forgot about #ifdef __cplusplus guard around 'extern "C"', so inclusion to C files has been broken.
------------------------------------------------------------------------
r23 | 2009-05-12 01:27:45 +0400 | 3 lines
[Issue 2] Always wrap <wcharîàž with external "C" {}.
It turns out that not only Visual Studio 6 requires this, but also newer versions when compiling for ARM.
------------------------------------------------------------------------
r22 | 2009-05-11 22:22:15 +0400 | 3 lines
[Issue 3] Visual Studio 6 and Embedded Visual C++ 4 doesn't realize that, e.g. char has the same size as __int8 so we give up on __intX for them.
his should close Issue 3 in issue tracker.
------------------------------------------------------------------------
r21 | 2008-07-17 09:47:22 +0400 | 4 lines
Get rid of these compiler warnings when compiling for 32-bit:
warning C4311: 'type cast' : pointer truncation from 'void *' to 'uintptr_t'
warning C4312: 'type cast' : conversion from 'uintptr_t' to 'const void *' of greater size
------------------------------------------------------------------------
r20 | 2007-10-09 16:54:27 +0400 | 2 lines
Better C99 conformance: macros for format specifiers should only be included in C++ implementations if __STDC_FORMAT_MACROS is defined before <inttypes.h> is included.
------------------------------------------------------------------------
r19 | 2007-07-04 02:14:40 +0400 | 3 lines
Explicitly cast to appropriate type INT8_MIN, INT16_MIN, INT32_MIN and INT64_MIN constants.
Due to their unusual definition in Visual Studio headers (-_Ix_MAX-1) they are propagated to int and thus do not have expected type, causing VS6 strict compiler to claim about type inconsistency.
------------------------------------------------------------------------
r18 | 2007-06-26 16:53:23 +0400 | 2 lines
Better handling of (U)INTx_C macros - now they generate constants of exact width.
------------------------------------------------------------------------
r17 | 2007-03-29 20:16:14 +0400 | 2 lines
Fix typo: Miscrosoft -> Microsoft.
------------------------------------------------------------------------
r16 | 2007-02-24 17:32:58 +0300 | 4 lines
Remove <BaseTsd.h> include, as it is not present in Visual Studio 2005 Epxress Edition and required only for INT_PTR and UINT_PTR types.
'intptr_t' and 'uintptr_t' types now defined explicitly with #ifdef _WIN64.
------------------------------------------------------------------------
r15 | 2007-02-11 20:53:05 +0300 | 2 lines
More correct fix for compilation under VS6.
------------------------------------------------------------------------
r14 | 2007-02-11 20:04:32 +0300 | 2 lines
Bugfix: fix compiling under VS6, when stdint.h enclosed in 'extern "C" {}'.
------------------------------------------------------------------------
r13 | 2006-12-13 16:53:11 +0300 | 2 lines
Make _inline modifier for imaxdiv default option. Use STATIC_IMAXDIV to make it static.
------------------------------------------------------------------------
r12 | 2006-12-13 16:42:24 +0300 | 2 lines
Error message changed: VC6 supported from now.
------------------------------------------------------------------------
r11 | 2006-12-13 16:39:33 +0300 | 2 lines
All (U)INT* types changed to (unsigned) __int*. This should make stdint.h compatible with VC6.
------------------------------------------------------------------------
r10 | 2006-12-13 16:20:57 +0300 | 3 lines
Added INLINE_IMAXDIV define switch.
If INLINE_IMAXDIV is defined imaxdiv() have static modifier. If not - it is _inline.
------------------------------------------------------------------------
r9 | 2006-12-13 15:53:52 +0300 | 2 lines
Error message for non-MSC compiler changed.
------------------------------------------------------------------------
r8 | 2006-12-13 12:47:48 +0300 | 2 lines
Added #ifndef for SIZE_MAX (it is defined in limits.h on MSVSC 8).
------------------------------------------------------------------------
r7 | 2006-12-13 01:08:02 +0300 | 2 lines
License chaged to BSD-derivative.
------------------------------------------------------------------------
r6 | 2006-12-13 00:53:20 +0300 | 2 lines
Added <wchar.h> include to avoid warnings when it is included after stdint.h.
------------------------------------------------------------------------
r5 | 2006-12-12 00:58:05 +0300 | 2 lines
BUGFIX: Definitions of INTPTR_MIN, INTPTR_MAX and UINTPTR_MAX for WIN32 and WIN64 was mixed up.
------------------------------------------------------------------------
r4 | 2006-12-12 00:51:55 +0300 | 2 lines
Rise #error if _MSC_VER is not defined. I.e. compiler other then Microsoft Visual C++ is used.
------------------------------------------------------------------------
r3 | 2006-12-11 22:54:14 +0300 | 2 lines
Added <limits.h> include to stdint.h.
------------------------------------------------------------------------
r2 | 2006-12-11 21:39:27 +0300 | 2 lines
Initial check in.
------------------------------------------------------------------------
r1 | 2006-12-11 21:30:23 +0300 | 1 line
Initial directory structure.
------------------------------------------------------------------------

View File

@ -0,0 +1,305 @@
// ISO C9x compliant inttypes.h for Microsoft Visual Studio
// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124
//
// Copyright (c) 2006 Alexander Chemeris
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. 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.
//
// 3. The name of the author may be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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 _MSC_VER // [
#error "Use this header only with Microsoft Visual C++ compilers!"
#endif // _MSC_VER ]
#ifndef _MSC_INTTYPES_H_ // [
#define _MSC_INTTYPES_H_
#if _MSC_VER > 1000
#pragma once
#endif
#include "stdint.h"
// 7.8 Format conversion of integer types
typedef struct {
intmax_t quot;
intmax_t rem;
} imaxdiv_t;
// 7.8.1 Macros for format specifiers
#if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) // [ See footnote 185 at page 198
// The fprintf macros for signed integers are:
#define PRId8 "d"
#define PRIi8 "i"
#define PRIdLEAST8 "d"
#define PRIiLEAST8 "i"
#define PRIdFAST8 "d"
#define PRIiFAST8 "i"
#define PRId16 "hd"
#define PRIi16 "hi"
#define PRIdLEAST16 "hd"
#define PRIiLEAST16 "hi"
#define PRIdFAST16 "hd"
#define PRIiFAST16 "hi"
#define PRId32 "I32d"
#define PRIi32 "I32i"
#define PRIdLEAST32 "I32d"
#define PRIiLEAST32 "I32i"
#define PRIdFAST32 "I32d"
#define PRIiFAST32 "I32i"
#define PRId64 "I64d"
#define PRIi64 "I64i"
#define PRIdLEAST64 "I64d"
#define PRIiLEAST64 "I64i"
#define PRIdFAST64 "I64d"
#define PRIiFAST64 "I64i"
#define PRIdMAX "I64d"
#define PRIiMAX "I64i"
#define PRIdPTR "Id"
#define PRIiPTR "Ii"
// The fprintf macros for unsigned integers are:
#define PRIo8 "o"
#define PRIu8 "u"
#define PRIx8 "x"
#define PRIX8 "X"
#define PRIoLEAST8 "o"
#define PRIuLEAST8 "u"
#define PRIxLEAST8 "x"
#define PRIXLEAST8 "X"
#define PRIoFAST8 "o"
#define PRIuFAST8 "u"
#define PRIxFAST8 "x"
#define PRIXFAST8 "X"
#define PRIo16 "ho"
#define PRIu16 "hu"
#define PRIx16 "hx"
#define PRIX16 "hX"
#define PRIoLEAST16 "ho"
#define PRIuLEAST16 "hu"
#define PRIxLEAST16 "hx"
#define PRIXLEAST16 "hX"
#define PRIoFAST16 "ho"
#define PRIuFAST16 "hu"
#define PRIxFAST16 "hx"
#define PRIXFAST16 "hX"
#define PRIo32 "I32o"
#define PRIu32 "I32u"
#define PRIx32 "I32x"
#define PRIX32 "I32X"
#define PRIoLEAST32 "I32o"
#define PRIuLEAST32 "I32u"
#define PRIxLEAST32 "I32x"
#define PRIXLEAST32 "I32X"
#define PRIoFAST32 "I32o"
#define PRIuFAST32 "I32u"
#define PRIxFAST32 "I32x"
#define PRIXFAST32 "I32X"
#define PRIo64 "I64o"
#define PRIu64 "I64u"
#define PRIx64 "I64x"
#define PRIX64 "I64X"
#define PRIoLEAST64 "I64o"
#define PRIuLEAST64 "I64u"
#define PRIxLEAST64 "I64x"
#define PRIXLEAST64 "I64X"
#define PRIoFAST64 "I64o"
#define PRIuFAST64 "I64u"
#define PRIxFAST64 "I64x"
#define PRIXFAST64 "I64X"
#define PRIoMAX "I64o"
#define PRIuMAX "I64u"
#define PRIxMAX "I64x"
#define PRIXMAX "I64X"
#define PRIoPTR "Io"
#define PRIuPTR "Iu"
#define PRIxPTR "Ix"
#define PRIXPTR "IX"
// The fscanf macros for signed integers are:
#define SCNd8 "d"
#define SCNi8 "i"
#define SCNdLEAST8 "d"
#define SCNiLEAST8 "i"
#define SCNdFAST8 "d"
#define SCNiFAST8 "i"
#define SCNd16 "hd"
#define SCNi16 "hi"
#define SCNdLEAST16 "hd"
#define SCNiLEAST16 "hi"
#define SCNdFAST16 "hd"
#define SCNiFAST16 "hi"
#define SCNd32 "ld"
#define SCNi32 "li"
#define SCNdLEAST32 "ld"
#define SCNiLEAST32 "li"
#define SCNdFAST32 "ld"
#define SCNiFAST32 "li"
#define SCNd64 "I64d"
#define SCNi64 "I64i"
#define SCNdLEAST64 "I64d"
#define SCNiLEAST64 "I64i"
#define SCNdFAST64 "I64d"
#define SCNiFAST64 "I64i"
#define SCNdMAX "I64d"
#define SCNiMAX "I64i"
#ifdef _WIN64 // [
# define SCNdPTR "I64d"
# define SCNiPTR "I64i"
#else // _WIN64 ][
# define SCNdPTR "ld"
# define SCNiPTR "li"
#endif // _WIN64 ]
// The fscanf macros for unsigned integers are:
#define SCNo8 "o"
#define SCNu8 "u"
#define SCNx8 "x"
#define SCNX8 "X"
#define SCNoLEAST8 "o"
#define SCNuLEAST8 "u"
#define SCNxLEAST8 "x"
#define SCNXLEAST8 "X"
#define SCNoFAST8 "o"
#define SCNuFAST8 "u"
#define SCNxFAST8 "x"
#define SCNXFAST8 "X"
#define SCNo16 "ho"
#define SCNu16 "hu"
#define SCNx16 "hx"
#define SCNX16 "hX"
#define SCNoLEAST16 "ho"
#define SCNuLEAST16 "hu"
#define SCNxLEAST16 "hx"
#define SCNXLEAST16 "hX"
#define SCNoFAST16 "ho"
#define SCNuFAST16 "hu"
#define SCNxFAST16 "hx"
#define SCNXFAST16 "hX"
#define SCNo32 "lo"
#define SCNu32 "lu"
#define SCNx32 "lx"
#define SCNX32 "lX"
#define SCNoLEAST32 "lo"
#define SCNuLEAST32 "lu"
#define SCNxLEAST32 "lx"
#define SCNXLEAST32 "lX"
#define SCNoFAST32 "lo"
#define SCNuFAST32 "lu"
#define SCNxFAST32 "lx"
#define SCNXFAST32 "lX"
#define SCNo64 "I64o"
#define SCNu64 "I64u"
#define SCNx64 "I64x"
#define SCNX64 "I64X"
#define SCNoLEAST64 "I64o"
#define SCNuLEAST64 "I64u"
#define SCNxLEAST64 "I64x"
#define SCNXLEAST64 "I64X"
#define SCNoFAST64 "I64o"
#define SCNuFAST64 "I64u"
#define SCNxFAST64 "I64x"
#define SCNXFAST64 "I64X"
#define SCNoMAX "I64o"
#define SCNuMAX "I64u"
#define SCNxMAX "I64x"
#define SCNXMAX "I64X"
#ifdef _WIN64 // [
# define SCNoPTR "I64o"
# define SCNuPTR "I64u"
# define SCNxPTR "I64x"
# define SCNXPTR "I64X"
#else // _WIN64 ][
# define SCNoPTR "lo"
# define SCNuPTR "lu"
# define SCNxPTR "lx"
# define SCNXPTR "lX"
#endif // _WIN64 ]
#endif // __STDC_FORMAT_MACROS ]
// 7.8.2 Functions for greatest-width integer types
// 7.8.2.1 The imaxabs function
#define imaxabs _abs64
// 7.8.2.2 The imaxdiv function
// This is modified version of div() function from Microsoft's div.c found
// in %MSVC.NET%\crt\src\div.c
#ifdef STATIC_IMAXDIV // [
static
#else // STATIC_IMAXDIV ][
_inline
#endif // STATIC_IMAXDIV ]
imaxdiv_t __cdecl imaxdiv(intmax_t numer, intmax_t denom)
{
imaxdiv_t result;
result.quot = numer / denom;
result.rem = numer % denom;
if (numer < 0 && result.rem > 0) {
// did division wrong; must fix up
++result.quot;
result.rem -= denom;
}
return result;
}
// 7.8.2.3 The strtoimax and strtoumax functions
#define strtoimax _strtoi64
#define strtoumax _strtoui64
// 7.8.2.4 The wcstoimax and wcstoumax functions
#define wcstoimax _wcstoi64
#define wcstoumax _wcstoui64
#endif // _MSC_INTTYPES_H_ ]

View File

@ -0,0 +1,247 @@
// ISO C9x compliant stdint.h for Microsoft Visual Studio
// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124
//
// Copyright (c) 2006-2008 Alexander Chemeris
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. 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.
//
// 3. The name of the author may be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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 _MSC_VER // [
#error "Use this header only with Microsoft Visual C++ compilers!"
#endif // _MSC_VER ]
#ifndef _MSC_STDINT_H_ // [
#define _MSC_STDINT_H_
#if _MSC_VER > 1000
#pragma once
#endif
#include <limits.h>
// For Visual Studio 6 in C++ mode and for many Visual Studio versions when
// compiling for ARM we should wrap <wchar.h> include with 'extern "C++" {}'
// or compiler give many errors like this:
// error C2733: second C linkage of overloaded function 'wmemchr' not allowed
#ifdef __cplusplus
extern "C" {
#endif
# include <wchar.h>
#ifdef __cplusplus
}
#endif
// Define _W64 macros to mark types changing their size, like intptr_t.
#ifndef _W64
# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300
# define _W64 __w64
# else
# define _W64
# endif
#endif
// 7.18.1 Integer types
// 7.18.1.1 Exact-width integer types
// Visual Studio 6 and Embedded Visual C++ 4 doesn't
// realize that, e.g. char has the same size as __int8
// so we give up on __intX for them.
#if (_MSC_VER < 1300)
typedef signed char int8_t;
typedef signed short int16_t;
typedef signed int int32_t;
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
#else
typedef signed __int8 int8_t;
typedef signed __int16 int16_t;
typedef signed __int32 int32_t;
typedef unsigned __int8 uint8_t;
typedef unsigned __int16 uint16_t;
typedef unsigned __int32 uint32_t;
#endif
typedef signed __int64 int64_t;
typedef unsigned __int64 uint64_t;
// 7.18.1.2 Minimum-width integer types
typedef int8_t int_least8_t;
typedef int16_t int_least16_t;
typedef int32_t int_least32_t;
typedef int64_t int_least64_t;
typedef uint8_t uint_least8_t;
typedef uint16_t uint_least16_t;
typedef uint32_t uint_least32_t;
typedef uint64_t uint_least64_t;
// 7.18.1.3 Fastest minimum-width integer types
typedef int8_t int_fast8_t;
typedef int16_t int_fast16_t;
typedef int32_t int_fast32_t;
typedef int64_t int_fast64_t;
typedef uint8_t uint_fast8_t;
typedef uint16_t uint_fast16_t;
typedef uint32_t uint_fast32_t;
typedef uint64_t uint_fast64_t;
// 7.18.1.4 Integer types capable of holding object pointers
#ifdef _WIN64 // [
typedef signed __int64 intptr_t;
typedef unsigned __int64 uintptr_t;
#else // _WIN64 ][
typedef _W64 signed int intptr_t;
typedef _W64 unsigned int uintptr_t;
#endif // _WIN64 ]
// 7.18.1.5 Greatest-width integer types
typedef int64_t intmax_t;
typedef uint64_t uintmax_t;
// 7.18.2 Limits of specified-width integer types
#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259
// 7.18.2.1 Limits of exact-width integer types
#define INT8_MIN ((int8_t)_I8_MIN)
#define INT8_MAX _I8_MAX
#define INT16_MIN ((int16_t)_I16_MIN)
#define INT16_MAX _I16_MAX
#define INT32_MIN ((int32_t)_I32_MIN)
#define INT32_MAX _I32_MAX
#define INT64_MIN ((int64_t)_I64_MIN)
#define INT64_MAX _I64_MAX
#define UINT8_MAX _UI8_MAX
#define UINT16_MAX _UI16_MAX
#define UINT32_MAX _UI32_MAX
#define UINT64_MAX _UI64_MAX
// 7.18.2.2 Limits of minimum-width integer types
#define INT_LEAST8_MIN INT8_MIN
#define INT_LEAST8_MAX INT8_MAX
#define INT_LEAST16_MIN INT16_MIN
#define INT_LEAST16_MAX INT16_MAX
#define INT_LEAST32_MIN INT32_MIN
#define INT_LEAST32_MAX INT32_MAX
#define INT_LEAST64_MIN INT64_MIN
#define INT_LEAST64_MAX INT64_MAX
#define UINT_LEAST8_MAX UINT8_MAX
#define UINT_LEAST16_MAX UINT16_MAX
#define UINT_LEAST32_MAX UINT32_MAX
#define UINT_LEAST64_MAX UINT64_MAX
// 7.18.2.3 Limits of fastest minimum-width integer types
#define INT_FAST8_MIN INT8_MIN
#define INT_FAST8_MAX INT8_MAX
#define INT_FAST16_MIN INT16_MIN
#define INT_FAST16_MAX INT16_MAX
#define INT_FAST32_MIN INT32_MIN
#define INT_FAST32_MAX INT32_MAX
#define INT_FAST64_MIN INT64_MIN
#define INT_FAST64_MAX INT64_MAX
#define UINT_FAST8_MAX UINT8_MAX
#define UINT_FAST16_MAX UINT16_MAX
#define UINT_FAST32_MAX UINT32_MAX
#define UINT_FAST64_MAX UINT64_MAX
// 7.18.2.4 Limits of integer types capable of holding object pointers
#ifdef _WIN64 // [
# define INTPTR_MIN INT64_MIN
# define INTPTR_MAX INT64_MAX
# define UINTPTR_MAX UINT64_MAX
#else // _WIN64 ][
# define INTPTR_MIN INT32_MIN
# define INTPTR_MAX INT32_MAX
# define UINTPTR_MAX UINT32_MAX
#endif // _WIN64 ]
// 7.18.2.5 Limits of greatest-width integer types
#define INTMAX_MIN INT64_MIN
#define INTMAX_MAX INT64_MAX
#define UINTMAX_MAX UINT64_MAX
// 7.18.3 Limits of other integer types
#ifdef _WIN64 // [
# define PTRDIFF_MIN _I64_MIN
# define PTRDIFF_MAX _I64_MAX
#else // _WIN64 ][
# define PTRDIFF_MIN _I32_MIN
# define PTRDIFF_MAX _I32_MAX
#endif // _WIN64 ]
#define SIG_ATOMIC_MIN INT_MIN
#define SIG_ATOMIC_MAX INT_MAX
#ifndef SIZE_MAX // [
# ifdef _WIN64 // [
# define SIZE_MAX _UI64_MAX
# else // _WIN64 ][
# define SIZE_MAX _UI32_MAX
# endif // _WIN64 ]
#endif // SIZE_MAX ]
// WCHAR_MIN and WCHAR_MAX are also defined in <wchar.h>
#ifndef WCHAR_MIN // [
# define WCHAR_MIN 0
#endif // WCHAR_MIN ]
#ifndef WCHAR_MAX // [
# define WCHAR_MAX _UI16_MAX
#endif // WCHAR_MAX ]
#define WINT_MIN 0
#define WINT_MAX _UI16_MAX
#endif // __STDC_LIMIT_MACROS ]
// 7.18.4 Limits of other integer types
#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260
// 7.18.4.1 Macros for minimum-width integer constants
#define INT8_C(val) val##i8
#define INT16_C(val) val##i16
#define INT32_C(val) val##i32
#define INT64_C(val) val##i64
#define UINT8_C(val) val##ui8
#define UINT16_C(val) val##ui16
#define UINT32_C(val) val##ui32
#define UINT64_C(val) val##ui64
// 7.18.4.2 Macros for greatest-width integer constants
#define INTMAX_C INT64_C
#define UINTMAX_C UINT64_C
#endif // __STDC_CONSTANT_MACROS ]
#endif // _MSC_STDINT_H_ ]

151
psx/mednadisc/endian.cpp Normal file
View File

@ -0,0 +1,151 @@
/* Mednafen - Multi-system Emulator
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdio.h>
#include "emuware/emuware.h"
#include "endian.h"
void Endian_A16_Swap(void *src, uint32 nelements)
{
uint32 i;
uint8 *nsrc = (uint8 *)src;
for(i = 0; i < nelements; i++)
{
uint8 tmp = nsrc[i * 2];
nsrc[i * 2] = nsrc[i * 2 + 1];
nsrc[i * 2 + 1] = tmp;
}
}
void Endian_A32_Swap(void *src, uint32 nelements)
{
uint32 i;
uint8 *nsrc = (uint8 *)src;
for(i = 0; i < nelements; i++)
{
uint8 tmp1 = nsrc[i * 4];
uint8 tmp2 = nsrc[i * 4 + 1];
nsrc[i * 4] = nsrc[i * 4 + 3];
nsrc[i * 4 + 1] = nsrc[i * 4 + 2];
nsrc[i * 4 + 2] = tmp2;
nsrc[i * 4 + 3] = tmp1;
}
}
void Endian_A64_Swap(void *src, uint32 nelements)
{
uint32 i;
uint8 *nsrc = (uint8 *)src;
for(i = 0; i < nelements; i++)
{
uint8 *base = &nsrc[i * 8];
for(int z = 0; z < 4; z++)
{
uint8 tmp = base[z];
base[z] = base[7 - z];
base[7 - z] = tmp;
}
}
}
void Endian_A16_NE_LE(void *src, uint32 nelements)
{
#ifdef MSB_FIRST
Endian_A16_Swap(src, nelements);
#endif
}
void Endian_A32_NE_LE(void *src, uint32 nelements)
{
#ifdef MSB_FIRST
Endian_A32_Swap(src, nelements);
#endif
}
void Endian_A64_NE_LE(void *src, uint32 nelements)
{
#ifdef MSB_FIRST
Endian_A64_Swap(src, nelements);
#endif
}
//
//
//
void Endian_A16_NE_BE(void *src, uint32 nelements)
{
#ifdef LSB_FIRST
Endian_A16_Swap(src, nelements);
#endif
}
void Endian_A32_NE_BE(void *src, uint32 nelements)
{
#ifdef LSB_FIRST
Endian_A32_Swap(src, nelements);
#endif
}
void Endian_A64_NE_BE(void *src, uint32 nelements)
{
#ifdef LSB_FIRST
Endian_A64_Swap(src, nelements);
#endif
}
static void FlipByteOrder(uint8 *src, uint32 count)
{
uint8 *start=src;
uint8 *end=src+count-1;
if((count&1) || !count) return; /* This shouldn't happen. */
count >>= 1;
while(count--)
{
uint8 tmp;
tmp=*end;
*end=*start;
*start=tmp;
end--;
start++;
}
}
void Endian_V_NE_LE(void *src, uint32 bytesize)
{
#ifdef MSB_FIRST
FlipByteOrder((uint8 *)src, bytesize);
#endif
}
void Endian_V_NE_BE(void *src, uint32 bytesize)
{
#ifdef LSB_FIRST
FlipByteOrder((uint8 *)src, bytesize);
#endif
}

296
psx/mednadisc/endian.h Normal file
View File

@ -0,0 +1,296 @@
#ifndef __MDFN_ENDIAN_H
#define __MDFN_ENDIAN_H
#pragma warning(once : 4519)
static INLINE uint32 BitsExtract(const uint8* ptr, const size_t bit_offset, const size_t bit_count)
{
uint32 ret = 0;
for(size_t x = 0; x < bit_count; x++)
{
size_t co = bit_offset + x;
bool b = (ptr[co >> 3] >> (co & 7)) & 1;
ret |= (uint64)b << x;
}
return ret;
}
static INLINE void BitsIntract(uint8* ptr, const size_t bit_offset, const size_t bit_count, uint32 value)
{
for(size_t x = 0; x < bit_count; x++)
{
size_t co = bit_offset + x;
bool b = (value >> x) & 1;
uint8 tmp = ptr[co >> 3];
tmp &= ~(1 << (co & 7));
tmp |= b << (co & 7);
ptr[co >> 3] = tmp;
}
}
/*
Regarding safety of calling MDFN_*sb<true> on dynamically-allocated memory with new uint8[], see C++ standard 3.7.3.1(i.e. it should be
safe provided the offsets into the memory are aligned/multiples of the MDFN_*sb access type). malloc()'d and calloc()'d
memory should be safe as well.
Statically-allocated arrays/memory should be unioned with a big POD type or C++11 "alignas"'d. (May need to audit code to ensure
this is being done).
*/
void Endian_A16_Swap(void *src, uint32 nelements);
void Endian_A32_Swap(void *src, uint32 nelements);
void Endian_A64_Swap(void *src, uint32 nelements);
void Endian_A16_NE_LE(void *src, uint32 nelements);
void Endian_A32_NE_LE(void *src, uint32 nelements);
void Endian_A64_NE_LE(void *src, uint32 nelements);
void Endian_A16_NE_BE(void *src, uint32 nelements);
void Endian_A32_NE_BE(void *src, uint32 nelements);
void Endian_A64_NE_BE(void *src, uint32 nelements);
void Endian_V_NE_LE(void *src, uint32 bytesize);
void Endian_V_NE_BE(void *src, uint32 bytesize);
static INLINE uint16 MDFN_bswap16(uint16 v)
{
return (v << 8) | (v >> 8);
}
static INLINE uint32 MDFN_bswap32(uint32 v)
{
return (v << 24) | ((v & 0xFF00) << 8) | ((v >> 8) & 0xFF00) | (v >> 24);
}
static INLINE uint64 MDFN_bswap64(uint64 v)
{
//octoshock edit
//return (v << 56) | (v >> 56) | ((v & 0xFF00) << 40) | ((v >> 40) & 0xFF00) | ((uint64)MDFN_bswap32(v >> 16) << 16);
return (v << 56) | (v >> 56) | ((v & 0xFF00) << 40) | ((v >> 40) & 0xFF00) | ((uint64)MDFN_bswap32(((uint32)v) >> 16) << 16);
}
#ifdef LSB_FIRST
#define MDFN_ENDIANH_IS_BIGENDIAN 0
#else
#define MDFN_ENDIANH_IS_BIGENDIAN 1
#endif
//
// X endian.
//
template<int isbigendian, typename T, bool aligned>
static INLINE T MDFN_deXsb(const void* ptr)
{
T tmp;
memcpy(&tmp, MDFN_ASSUME_ALIGNED(ptr, (aligned ? sizeof(T) : 1)), sizeof(T));
if(isbigendian != -1 && isbigendian != MDFN_ENDIANH_IS_BIGENDIAN)
{
static_assert(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8, "Gummy penguins.");
if(sizeof(T) == 8)
return (T)MDFN_bswap64(tmp);
else if(sizeof(T) == 4)
return (T)MDFN_bswap32(tmp);
else if(sizeof(T) == 2)
return (T)MDFN_bswap16(tmp);
}
return tmp;
}
//
// Native endian.
//
template<typename T, bool aligned = false>
static INLINE T MDFN_densb(const void* ptr)
{
return MDFN_deXsb<-1, T, aligned>(ptr);
}
//
// Little endian.
//
template<typename T, bool aligned = false>
static INLINE T MDFN_delsb(const void* ptr)
{
return MDFN_deXsb<0, T, aligned>(ptr);
}
template<bool aligned = false>
static INLINE uint16 MDFN_de16lsb(const void* ptr)
{
return MDFN_delsb<uint16, aligned>(ptr);
}
static INLINE uint32 MDFN_de24lsb(const void* ptr)
{
const uint8* morp = (const uint8*)ptr;
return(morp[0]|(morp[1]<<8)|(morp[2]<<16));
}
template<bool aligned = false>
static INLINE uint32 MDFN_de32lsb(const void* ptr)
{
return MDFN_delsb<uint32, aligned>(ptr);
}
template<bool aligned = false>
static INLINE uint64 MDFN_de64lsb(const void* ptr)
{
return MDFN_delsb<uint64, aligned>(ptr);
}
//
// Big endian.
//
template<typename T, bool aligned = false>
static INLINE T MDFN_demsb(const void* ptr)
{
return MDFN_deXsb<1, T, aligned>(ptr);
}
template<bool aligned = false>
static INLINE uint16 MDFN_de16msb(const void* ptr)
{
return MDFN_demsb<uint16, aligned>(ptr);
}
static INLINE uint32 MDFN_de24msb(const void* ptr)
{
const uint8* morp = (const uint8*)ptr;
return((morp[2]<<0)|(morp[1]<<8)|(morp[0]<<16));
}
template<bool aligned = false>
static INLINE uint32 MDFN_de32msb(const void* ptr)
{
return MDFN_demsb<uint32, aligned>(ptr);
}
template<bool aligned = false>
static INLINE uint64 MDFN_de64msb(const void* ptr)
{
return MDFN_demsb<uint64, aligned>(ptr);
}
//
//
//
//
//
//
//
//
//
// X endian.
//
template<int isbigendian, typename T, bool aligned>
static INLINE void MDFN_enXsb(void* ptr, T value)
{
T tmp = value;
if(isbigendian != -1 && isbigendian != MDFN_ENDIANH_IS_BIGENDIAN)
{
static_assert(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8, "Gummy penguins.");
if(sizeof(T) == 8)
tmp = (T)MDFN_bswap64(value);
else if(sizeof(T) == 4)
tmp = (T)MDFN_bswap32(value);
else if(sizeof(T) == 2)
tmp = (T)MDFN_bswap16(value);
}
memcpy(MDFN_ASSUME_ALIGNED(ptr, (aligned ? sizeof(T) : 1)), &tmp, sizeof(T));
}
//
// Native endian.
//
template<typename T, bool aligned = false>
static INLINE void MDFN_ennsb(void* ptr, T value)
{
MDFN_enXsb<-1, T, aligned>(ptr, value);
}
//
// Little endian.
//
template<typename T, bool aligned = false>
static INLINE void MDFN_enlsb(void* ptr, T value)
{
MDFN_enXsb<0, T, aligned>(ptr, value);
}
template<bool aligned = false>
static INLINE void MDFN_en16lsb(void* ptr, uint16 value)
{
MDFN_enlsb<uint16, aligned>(ptr, value);
}
static INLINE void MDFN_en24lsb(void* ptr, uint32 value)
{
uint8* morp = (uint8*)ptr;
morp[0] = value;
morp[1] = value >> 8;
morp[2] = value >> 16;
}
template<bool aligned = false>
static INLINE void MDFN_en32lsb(void* ptr, uint32 value)
{
MDFN_enlsb<uint32, aligned>(ptr, value);
}
template<bool aligned = false>
static INLINE void MDFN_en64lsb(void* ptr, uint64 value)
{
MDFN_enlsb<uint64, aligned>(ptr, value);
}
//
// Big endian.
//
template<typename T, bool aligned = false>
static INLINE void MDFN_enmsb(void* ptr, T value)
{
MDFN_enXsb<1, T, aligned>(ptr, value);
}
template<bool aligned = false>
static INLINE void MDFN_en16msb(void* ptr, uint16 value)
{
MDFN_enmsb<uint16, aligned>(ptr, value);
}
static INLINE void MDFN_en24msb(void* ptr, uint32 value)
{
uint8* morp = (uint8*)ptr;
morp[0] = value;
morp[1] = value >> 8;
morp[2] = value >> 16;
}
template<bool aligned = false>
static INLINE void MDFN_en32msb(void* ptr, uint32 value)
{
MDFN_enmsb<uint32, aligned>(ptr, value);
}
template<bool aligned = false>
static INLINE void MDFN_en64msb(void* ptr, uint64 value)
{
MDFN_enmsb<uint64, aligned>(ptr, value);
}
#endif

155
psx/mednadisc/error.cpp Normal file
View File

@ -0,0 +1,155 @@
/* Mednafen - Multi-system Emulator
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include "emuware/emuware.h"
#include "error.h"
MDFN_Error::MDFN_Error() noexcept
{
abort();
}
MDFN_Error::MDFN_Error(int errno_code_new, const char *format, ...) noexcept
{
errno_code = errno_code_new;
error_message = NULL;
int size = 128;
for(;;) {
va_list ap;
va_start(ap, format);
error_message = (char*)malloc(size);
size *= 2;
int ret = vsprintf(error_message, format, ap);
va_end(ap);
if(ret>=0)
break;
free(error_message);
}
}
MDFN_Error::MDFN_Error(const ErrnoHolder &enh)
{
errno_code = enh.Errno();
int size = 128;
for(;;) {
error_message = (char*)malloc(size);
size *= 2;
int ret = sprintf("%s", enh.StrError());
if(ret>=0)
break;
free(error_message);
}
}
MDFN_Error::~MDFN_Error() noexcept
{
if(error_message)
{
free(error_message);
error_message = NULL;
}
}
MDFN_Error::MDFN_Error(const MDFN_Error &ze_error) noexcept
{
if(ze_error.error_message)
error_message = strdup(ze_error.error_message);
else
error_message = NULL;
errno_code = ze_error.errno_code;
}
MDFN_Error& MDFN_Error::operator=(const MDFN_Error &ze_error) noexcept
{
char *new_error_message = ze_error.error_message ? strdup(ze_error.error_message) : NULL;
int new_errno_code = ze_error.errno_code;
if(error_message)
free(error_message);
error_message = new_error_message;
errno_code = new_errno_code;
return(*this);
}
const char * MDFN_Error::what(void) const noexcept
{
if(!error_message)
return("Error allocating memory for the error message!");
return(error_message);
}
int MDFN_Error::GetErrno(void) const noexcept
{
return(errno_code);
}
static const char *srr_wrap(int ret, const char *local_strerror)
{
if(ret == -1)
return("ERROR IN strerror_r()!!!");
return(local_strerror);
}
static const char *srr_wrap(const char *ret, const char *local_strerror)
{
if(ret == NULL)
return("ERROR IN strerror_r()!!!");
return(ret);
}
void ErrnoHolder::SetErrno(int the_errno)
{
local_errno = the_errno;
if(the_errno == 0)
local_strerror[0] = 0;
else
{
#ifdef HAVE_STRERROR_R
const char *retv;
retv = srr_wrap(strerror_r(the_errno, local_strerror, 256), local_strerror);
if(retv != local_strerror)
strncpy(local_strerror, retv, 255);
#else // No strerror_r :(
strncpy(local_strerror, strerror(the_errno), 255);
#endif
local_strerror[255] = 0;
}
}

75
psx/mednadisc/error.h Normal file
View File

@ -0,0 +1,75 @@
#ifndef __MDFN_ERROR_H
#define __MDFN_ERROR_H
#include <errno.h>
#include <string.h>
#include <exception>
#ifdef __cplusplus
class ErrnoHolder;
class MDFN_Error : public std::exception
{
public:
MDFN_Error() noexcept;
MDFN_Error(int errno_code_new, const char *format, ...) noexcept MDFN_FORMATSTR(gnu_printf, 3, 4);
MDFN_Error(const ErrnoHolder &enh);
~MDFN_Error() noexcept;
MDFN_Error(const MDFN_Error &ze_error) noexcept;
MDFN_Error & operator=(const MDFN_Error &ze_error) noexcept;
virtual const char *what(void) const noexcept;
int GetErrno(void) const noexcept;
private:
int errno_code;
char *error_message;
};
class ErrnoHolder
{
public:
ErrnoHolder()
{
//SetErrno(0);
local_errno = 0;
local_strerror[0] = 0;
}
ErrnoHolder(int the_errno)
{
SetErrno(the_errno);
}
inline int Errno(void) const
{
return(local_errno);
}
const char *StrError(void) const
{
return(local_strerror);
}
void operator=(int the_errno)
{
SetErrno(the_errno);
}
private:
void SetErrno(int the_errno);
int local_errno;
char local_strerror[256];
};
#endif
#endif

366
psx/mednadisc/general.cpp Normal file
View File

@ -0,0 +1,366 @@
/* Mednafen - Multi-system Emulator
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "emuware/emuware.h"
#include <string.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string>
#include <map>
#include "general.h"
#include "error.h"
#define _(X) X
#ifdef WIN32
#define PSS "\\"
#else
#define PSS "/"
#endif
static struct {
bool untrusted_fip_check;
} s_settings;
using namespace std;
static string BaseDirectory;
static string FileBase;
static string FileExt; /* Includes the . character, as in ".nes" */
static string FileBaseDirectory;
void MDFN_SetBaseDirectory(const std::string& dir)
{
BaseDirectory = string(dir);
}
std::string MDFN_GetBaseDirectory(void)
{
return BaseDirectory;
}
// Really dumb, maybe we should use boost?
static bool IsAbsolutePath(const char *path)
{
#if PSS_STYLE==4
if(path[0] == ':')
#elif PSS_STYLE==1
if(path[0] == '/')
#else
if(path[0] == '\\'
#if PSS_STYLE!=3
|| path[0] == '/'
#endif
)
#endif
{
return(true);
}
#if defined(WIN32) || defined(DOS)
if((path[0] >= 'a' && path[0] <= 'z') || (path[0] >= 'A' && path[0] <= 'Z'))
{
if(path[1] == ':')
{
return(true);
}
}
#endif
return(false);
}
static bool IsAbsolutePath(const std::string &path)
{
return(IsAbsolutePath(path.c_str()));
}
bool MDFN_IsFIROPSafe(const std::string &path)
{
//
// First, check for any 8-bit characters, and print a warning about portability.
//
for(size_t x = 0; x < path.size(); x++)
{
if(path[x] & 0x80)
{
printf(_("WARNING: Referenced path \"%s\" contains at least one 8-bit non-ASCII character; this may cause portability issues.\n"), path.c_str());
break;
}
}
// We could make this more OS-specific, but it shouldn't hurt to try to weed out usage of characters that are path
// separators in one OS but not in another, and we'd also run more of a risk of missing a special path separator case
// in some OS.
if(!s_settings.untrusted_fip_check)
return(true);
if(path.find('\0') != string::npos)
return(false);
if(path.find(':') != string::npos)
return(false);
if(path.find('\\') != string::npos)
return(false);
if(path.find('/') != string::npos)
return(false);
#if defined(DOS) || defined(WIN32)
//
// http://support.microsoft.com/kb/74496
//
{
static const char* dev_names[] =
{
"CON", "PRN", "AUX", "CLOCK$", "NUL", "COM1", "COM2", "COM3", "COM4", "LPT1", "LPT2", "LPT3", NULL
};
for(const char** ls = dev_names; *ls != NULL; ls++)
{
if(!strcasecmp(*ls, path.c_str()))
return(false);
}
}
#endif
return(true);
}
void MDFN_GetFilePathComponents(const std::string &file_path, std::string *dir_path_out, std::string *file_base_out, std::string *file_ext_out)
{
size_t final_ds; // in file_path
string file_name;
size_t fn_final_dot; // in local var file_name
// Temporary output:
string dir_path, file_base, file_ext;
#if PSS_STYLE==4
final_ds = file_path.find_last_of(':');
#elif PSS_STYLE==1
final_ds = file_path.find_last_of('/');
#else
final_ds = file_path.find_last_of('\\');
#if PSS_STYLE!=3
{
size_t alt_final_ds = file_path.find_last_of('/');
if(final_ds == string::npos || (alt_final_ds != string::npos && alt_final_ds > final_ds))
final_ds = alt_final_ds;
}
#endif
#endif
if(final_ds == string::npos)
{
dir_path = string(".");
file_name = file_path;
}
else
{
dir_path = file_path.substr(0, final_ds);
file_name = file_path.substr(final_ds + 1);
}
fn_final_dot = file_name.find_last_of('.');
if(fn_final_dot != string::npos)
{
file_base = file_name.substr(0, fn_final_dot);
file_ext = file_name.substr(fn_final_dot);
}
else
{
file_base = file_name;
file_ext = string("");
}
if(dir_path_out)
*dir_path_out = dir_path;
if(file_base_out)
*file_base_out = file_base;
if(file_ext_out)
*file_ext_out = file_ext;
}
std::string MDFN_EvalFIP(const std::string &dir_path, const std::string &rel_path, bool skip_safety_check)
{
if(!skip_safety_check && !MDFN_IsFIROPSafe(rel_path))
throw MDFN_Error(0, _("Referenced path \"%s\" is potentially unsafe. See \"filesys.untrusted_fip_check\" setting.\n"), rel_path.c_str());
if(IsAbsolutePath(rel_path.c_str()))
return(rel_path);
else
{
return(dir_path + std::string(PSS) + rel_path);
}
}
typedef std::map<char, std::string> FSMap;
static std::string EvalPathFS(const std::string &fstring, /*const (won't work because entry created if char doesn't exist) */ FSMap &fmap)
{
std::string ret = "";
const char *str = fstring.c_str();
bool in_spec = false;
while(*str)
{
int c = *str;
if(!in_spec && c == '%')
in_spec = true;
else if(in_spec == true)
{
if(c == '%')
ret = ret + std::string("%");
else
ret = ret + fmap[(char)c];
in_spec = false;
}
else
{
char ct[2];
ct[0] = c;
ct[1] = 0;
ret += std::string(ct);
}
str++;
}
return(ret);
}
#if 0
static void CreateMissingDirs(const char *path)
{
const char *s = path;
bool first_psep = true;
char last_char = 0;
const char char_test1 = '/', char_test2 = '/';
while(*s)
{
if(*s == char_test1 || *s == char_test2)
{
if(last_char != *s) //char_test1 && last_char != char_test2)
{
if(!first_psep)
{
char tmpbuf[(s - path) + 1];
tmpbuf[s - path] = 0;
strncpy(tmpbuf, path, s - path);
puts(tmpbuf);
//MDFN_mkdir(tmpbuf, S_IRWXU);
}
}
first_psep = false;
}
last_char = *s;
s++;
}
}
#endif
const char * GetFNComponent(const char *str)
{
const char *tp1;
#if PSS_STYLE==4
tp1=((char *)strrchr(str,':'));
#elif PSS_STYLE==1
tp1=((char *)strrchr(str,'/'));
#else
tp1=((char *)strrchr(str,'\\'));
#if PSS_STYLE!=3
{
const char *tp3;
tp3=((char *)strrchr(str,'/'));
if(tp1<tp3) tp1=tp3;
}
#endif
#endif
if(tp1)
return(tp1+1);
else
return(str);
}
void GetFileBase(const char *f)
{
const char *tp1,*tp3;
#if PSS_STYLE==4
tp1=((char *)strrchr(f,':'));
#elif PSS_STYLE==1
tp1=((char *)strrchr(f,'/'));
#else
tp1=((char *)strrchr(f,'\\'));
#if PSS_STYLE!=3
tp3=((char *)strrchr(f,'/'));
if(tp1<tp3) tp1=tp3;
#endif
#endif
if(!tp1)
{
tp1=f;
FileBaseDirectory = ".";
}
else
{
char* tmpfn = (char*)alloca(tp1 - f + 1);
memcpy(tmpfn,f,tp1-f);
tmpfn[tp1-f]=0;
FileBaseDirectory = string(tmpfn);
tp1++;
}
if(((tp3=strrchr(f,'.'))!=NULL) && (tp3>tp1))
{
char* tmpbase = (char*)alloca(tp3 - tp1 + 1);
memcpy(tmpbase,tp1,tp3-tp1);
tmpbase[tp3-tp1]=0;
FileBase = string(tmpbase);
FileExt = string(tp3);
}
else
{
FileBase = string(tp1);
FileExt = "";
}
}

52
psx/mednadisc/general.h Normal file
View File

@ -0,0 +1,52 @@
#ifndef _GENERAL_H
#define _GENERAL_H
#include <string>
#if 0
class FilePathMaker
{
void SetBaseDirectory(const char* path);
std::string GetBaseDirectory(void);
void BuildPath(unsigned type, int id1, const char* cd1);
static void GetFileBase(
};
#endif
void MDFN_SetBaseDirectory(const std::string& dir);
std::string MDFN_GetBaseDirectory(void);
void GetFileBase(const char *f);
// File-inclusion for-read-only path, for PSF and CUE/TOC sheet usage.
bool MDFN_IsFIROPSafe(const std::string &path);
std::string MDFN_MakeFName(int type, int id1, const char *cd1);
typedef enum
{
MDFNMKF_STATE = 0,
MDFNMKF_SNAP,
MDFNMKF_SAV,
MDFNMKF_CHEAT,
MDFNMKF_PALETTE,
MDFNMKF_IPS,
MDFNMKF_MOVIE,
MDFNMKF_AUX,
MDFNMKF_SNAP_DAT,
MDFNMKF_CHEAT_TMP,
MDFNMKF_FIRMWARE
} MakeFName_Type;
std::string MDFN_MakeFName(MakeFName_Type type, int id1, const char *cd1);
INLINE std::string MDFN_MakeFName(MakeFName_Type type, int id1, const std::string& cd1) { return MDFN_MakeFName(type, id1, cd1.c_str()); }
const char * GetFNComponent(const char *str);
void MDFN_GetFilePathComponents(const std::string &file_path, std::string *dir_path_out, std::string *file_base_out = NULL, std::string *file_ext_out = NULL);
std::string MDFN_EvalFIP(const std::string &dir_path, const std::string &rel_path, bool skip_safety_check = false);
#endif

87
psx/mednadisc/math_ops.h Normal file
View File

@ -0,0 +1,87 @@
#ifndef __MDFN_MATH_OPS_H
#define __MDFN_MATH_OPS_H
// Source: http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
// Rounds up to the nearest power of 2.
static INLINE uint64 round_up_pow2(uint64 v)
{
v--;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v |= v >> 32;
v++;
v += (v == 0);
return(v);
}
static INLINE uint32 uilog2(uint32 v)
{
// http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogDeBruijn
static const uint32 MultiplyDeBruijnBitPosition[32] =
{
0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30,
8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31
};
v |= v >> 1; // first round down to one less than a power of 2
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
return MultiplyDeBruijnBitPosition[(uint32_t)(v * 0x07C4ACDDU) >> 27];
}
// Some compilers' optimizers and some platforms might fubar the generated code from these macros,
// so some tests are run in...tests.cpp
#define sign_8_to_s16(_value) ((int16)(int8)(_value))
#define sign_9_to_s16(_value) (((int16)((unsigned int)(_value) << 7)) >> 7)
#define sign_10_to_s16(_value) (((int16)((uint32)(_value) << 6)) >> 6)
#define sign_11_to_s16(_value) (((int16)((uint32)(_value) << 5)) >> 5)
#define sign_12_to_s16(_value) (((int16)((uint32)(_value) << 4)) >> 4)
#define sign_13_to_s16(_value) (((int16)((uint32)(_value) << 3)) >> 3)
#define sign_14_to_s16(_value) (((int16)((uint32)(_value) << 2)) >> 2)
#define sign_15_to_s16(_value) (((int16)((uint32)(_value) << 1)) >> 1)
// This obviously won't convert higher-than-32 bit numbers to signed 32-bit ;)
// Also, this shouldn't be used for 8-bit and 16-bit signed numbers, since you can
// convert those faster with typecasts...
#define sign_x_to_s32(_bits, _value) (((int32)((uint32)(_value) << (32 - _bits))) >> (32 - _bits))
static INLINE int32 clamp_to_u8(int32 i)
{
if(i & 0xFFFFFF00)
i = (((~i) >> 30) & 0xFF);
return(i);
}
static INLINE int32 clamp_to_u16(int32 i)
{
if(i & 0xFFFF0000)
i = (((~i) >> 31) & 0xFFFF);
return(i);
}
template<typename T, typename U, typename V> static INLINE void clamp(T *val, U minimum, V maximum)
{
if(*val < minimum)
{
//printf("Warning: clamping to minimum(%d)\n", (int)minimum);
*val = minimum;
}
if(*val > maximum)
{
//printf("Warning: clamping to maximum(%d)\n", (int)maximum);
*val = maximum;
}
}
#endif

View File

@ -0,0 +1,560 @@
/*
* Copyright 2001-2004 Unicode, Inc.
*
* Disclaimer
*
* This source code is provided as is by Unicode, Inc. No claims are
* made as to fitness for any particular purpose. No warranties of any
* kind are expressed or implied. The recipient agrees to determine
* applicability of information provided. If this file has been
* purchased on magnetic or optical media from Unicode, Inc., the
* sole remedy for any claim will be exchange of defective media
* within 90 days of receipt.
*
* Limitations on Rights to Redistribute This Code
*
* Unicode, Inc. hereby grants the right to freely use the information
* supplied in this file in the creation of products supporting the
* Unicode Standard, and to make copies of this file in any form
* for internal or external distribution as long as this notice
* remains attached.
*/
/* ---------------------------------------------------------------------
Conversions between UTF32, UTF-16, and UTF-8. Source code file.
Author: Mark E. Davis, 1994.
Rev History: Rick McGowan, fixes & updates May 2001.
Sept 2001: fixed const & error conditions per
mods suggested by S. Parent & A. Lillich.
June 2002: Tim Dodd added detection and handling of incomplete
source sequences, enhanced error detection, added casts
to eliminate compiler warnings.
July 2003: slight mods to back out aggressive FFFE detection.
Jan 2004: updated switches in from-UTF8 conversions.
Oct 2004: updated to use UNI_MAX_LEGAL_UTF32 in UTF-32 conversions.
See the header file "ConvertUTF.h" for complete documentation.
------------------------------------------------------------------------ */
#include "../types.h"
#include "ConvertUTF.h"
#ifdef CVTUTF_DEBUG
#include <stdio.h>
#endif
#include <string.h>
#include <stdlib.h>
static const int halfShift = 10; /* used for shifting by 10 bits */
static const UTF32 halfBase = 0x0010000UL;
static const UTF32 halfMask = 0x3FFUL;
#define UNI_SUR_HIGH_START (UTF32)0xD800
#define UNI_SUR_HIGH_END (UTF32)0xDBFF
#define UNI_SUR_LOW_START (UTF32)0xDC00
#define UNI_SUR_LOW_END (UTF32)0xDFFF
#define false 0
#define true 1
/* --------------------------------------------------------------------- */
ConversionResult ConvertUTF32toUTF16 (
const UTF32** sourceStart, const UTF32* sourceEnd,
UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) {
ConversionResult result = conversionOK;
const UTF32* source = *sourceStart;
UTF16* target = *targetStart;
while (source < sourceEnd) {
UTF32 ch;
if (target >= targetEnd) {
result = targetExhausted; break;
}
ch = *source++;
if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */
/* UTF-16 surrogate values are illegal in UTF-32; 0xffff or 0xfffe are both reserved values */
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
if (flags == strictConversion) {
--source; /* return to the illegal value itself */
result = sourceIllegal;
break;
} else {
*target++ = UNI_REPLACEMENT_CHAR;
}
} else {
*target++ = (UTF16)ch; /* normal case */
}
} else if (ch > UNI_MAX_LEGAL_UTF32) {
if (flags == strictConversion) {
result = sourceIllegal;
} else {
*target++ = UNI_REPLACEMENT_CHAR;
}
} else {
/* target is a character in range 0xFFFF - 0x10FFFF. */
if (target + 1 >= targetEnd) {
--source; /* Back up source pointer! */
result = targetExhausted; break;
}
ch -= halfBase;
*target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START);
*target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START);
}
}
*sourceStart = source;
*targetStart = target;
return result;
}
/* --------------------------------------------------------------------- */
ConversionResult ConvertUTF16toUTF32 (
const UTF16** sourceStart, const UTF16* sourceEnd,
UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags) {
ConversionResult result = conversionOK;
const UTF16* source = *sourceStart;
UTF32* target = *targetStart;
UTF32 ch, ch2;
while (source < sourceEnd) {
const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */
ch = *source++;
/* If we have a surrogate pair, convert to UTF32 first. */
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) {
/* If the 16 bits following the high surrogate are in the source buffer... */
if (source < sourceEnd) {
ch2 = *source;
/* If it's a low surrogate, convert to UTF32. */
if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) {
ch = ((ch - UNI_SUR_HIGH_START) << halfShift)
+ (ch2 - UNI_SUR_LOW_START) + halfBase;
++source;
} else if (flags == strictConversion) { /* it's an unpaired high surrogate */
--source; /* return to the illegal value itself */
result = sourceIllegal;
break;
}
} else { /* We don't have the 16 bits following the high surrogate. */
--source; /* return to the high surrogate */
result = sourceExhausted;
break;
}
} else if (flags == strictConversion) {
/* UTF-16 surrogate values are illegal in UTF-32 */
if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) {
--source; /* return to the illegal value itself */
result = sourceIllegal;
break;
}
}
if (target >= targetEnd) {
source = oldSource; /* Back up source pointer! */
result = targetExhausted; break;
}
*target++ = ch;
}
*sourceStart = source;
*targetStart = target;
#ifdef CVTUTF_DEBUG
if (result == sourceIllegal) {
fprintf(stderr, "ConvertUTF16toUTF32 illegal seq 0x%04x,%04x\n", ch, ch2);
fflush(stderr);
}
#endif
return result;
}
/* --------------------------------------------------------------------- */
/*
* Index into the table below with the first byte of a UTF-8 sequence to
* get the number of trailing bytes that are supposed to follow it.
* Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is
* left as-is for anyone who may want to do such conversion, which was
* allowed in earlier algorithms.
*/
static const char trailingBytesForUTF8[256] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
};
/*
* Magic values subtracted from a buffer value during UTF8 conversion.
* This table contains as many values as there might be trailing bytes
* in a UTF-8 sequence.
*/
static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL,
0x03C82080UL, 0xFA082080UL, 0x82082080UL };
/*
* Once the bits are split out into bytes of UTF-8, this is a mask OR-ed
* into the first byte, depending on how many bytes follow. There are
* as many entries in this table as there are UTF-8 sequence types.
* (I.e., one byte sequence, two byte... etc.). Remember that sequencs
* for *legal* UTF-8 will be 4 or fewer bytes total.
*/
static const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
/* --------------------------------------------------------------------- */
/* The interface converts a whole buffer to avoid function-call overhead.
* Constants have been gathered. Loops & conditionals have been removed as
* much as possible for efficiency, in favor of drop-through switches.
* (See "Note A" at the bottom of the file for equivalent code.)
* If your compiler supports it, the "isLegalUTF8" call can be turned
* into an inline function.
*/
/* --------------------------------------------------------------------- */
ConversionResult ConvertUTF16toUTF8 (
const UTF16** sourceStart, const UTF16* sourceEnd,
UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) {
ConversionResult result = conversionOK;
const UTF16* source = *sourceStart;
UTF8* target = *targetStart;
while (source < sourceEnd) {
UTF32 ch;
unsigned short bytesToWrite = 0;
const UTF32 byteMask = 0xBF;
const UTF32 byteMark = 0x80;
const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */
ch = *source++;
/* If we have a surrogate pair, convert to UTF32 first. */
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) {
/* If the 16 bits following the high surrogate are in the source buffer... */
if (source < sourceEnd) {
UTF32 ch2 = *source;
/* If it's a low surrogate, convert to UTF32. */
if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) {
ch = ((ch - UNI_SUR_HIGH_START) << halfShift)
+ (ch2 - UNI_SUR_LOW_START) + halfBase;
++source;
} else if (flags == strictConversion) { /* it's an unpaired high surrogate */
--source; /* return to the illegal value itself */
result = sourceIllegal;
break;
}
} else { /* We don't have the 16 bits following the high surrogate. */
--source; /* return to the high surrogate */
result = sourceExhausted;
break;
}
} else if (flags == strictConversion) {
/* UTF-16 surrogate values are illegal in UTF-32 */
if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) {
--source; /* return to the illegal value itself */
result = sourceIllegal;
break;
}
}
/* Figure out how many bytes the result will require */
if (ch < (UTF32)0x80) { bytesToWrite = 1;
} else if (ch < (UTF32)0x800) { bytesToWrite = 2;
} else if (ch < (UTF32)0x10000) { bytesToWrite = 3;
} else if (ch < (UTF32)0x110000) { bytesToWrite = 4;
} else { bytesToWrite = 3;
ch = UNI_REPLACEMENT_CHAR;
}
target += bytesToWrite;
if (target > targetEnd) {
source = oldSource; /* Back up source pointer! */
target -= bytesToWrite; result = targetExhausted; break;
}
switch (bytesToWrite) { /* note: everything falls through. */
case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
case 1: *--target = (UTF8)(ch | firstByteMark[bytesToWrite]);
}
target += bytesToWrite;
}
*sourceStart = source;
*targetStart = target;
return result;
}
/* --------------------------------------------------------------------- */
/*
* Utility routine to tell whether a sequence of bytes is legal UTF-8.
* This must be called with the length pre-determined by the first byte.
* If not calling this from ConvertUTF8to*, then the length can be set by:
* length = trailingBytesForUTF8[*source]+1;
* and the sequence is illegal right away if there aren't that many bytes
* available.
* If presented with a length > 4, this returns false. The Unicode
* definition of UTF-8 goes up to 4-byte sequences.
*/
static bool isLegalUTF8(const UTF8 *source, int length) {
UTF8 a;
const UTF8 *srcptr = source+length;
switch (length) {
default: return false;
/* Everything else falls through when "true"... */
case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
case 2: if ((a = (*--srcptr)) > 0xBF) return false;
switch (*source) {
/* no fall-through in this inner switch */
case 0xE0: if (a < 0xA0) return false; break;
case 0xED: if (a > 0x9F) return false; break;
case 0xF0: if (a < 0x90) return false; break;
case 0xF4: if (a > 0x8F) return false; break;
default: if (a < 0x80) return false;
}
case 1: if (*source >= 0x80 && *source < 0xC2) return false;
}
if (*source > 0xF4) return false;
return true;
}
/* --------------------------------------------------------------------- */
/*
* Exported function to return whether a UTF-8 sequence is legal or not.
* This is not used here; it's just exported.
*/
bool isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd) {
int length = trailingBytesForUTF8[*source]+1;
if (source+length > sourceEnd) {
return false;
}
return isLegalUTF8(source, length);
}
/* --------------------------------------------------------------------- */
ConversionResult ConvertUTF8toUTF16 (
const UTF8** sourceStart, const UTF8* sourceEnd,
UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) {
ConversionResult result = conversionOK;
const UTF8* source = *sourceStart;
UTF16* target = *targetStart;
while (source < sourceEnd) {
UTF32 ch = 0;
unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
if (source + extraBytesToRead >= sourceEnd) {
result = sourceExhausted; break;
}
/* Do this check whether lenient or strict */
if (! isLegalUTF8(source, extraBytesToRead+1)) {
result = sourceIllegal;
break;
}
/*
* The cases all fall through. See "Note A" below.
*/
switch (extraBytesToRead) {
case 5: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */
case 4: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */
case 3: ch += *source++; ch <<= 6;
case 2: ch += *source++; ch <<= 6;
case 1: ch += *source++; ch <<= 6;
case 0: ch += *source++;
}
ch -= offsetsFromUTF8[extraBytesToRead];
if (target >= targetEnd) {
source -= (extraBytesToRead+1); /* Back up source pointer! */
result = targetExhausted; break;
}
if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */
/* UTF-16 surrogate values are illegal in UTF-32 */
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
if (flags == strictConversion) {
source -= (extraBytesToRead+1); /* return to the illegal value itself */
result = sourceIllegal;
break;
} else {
*target++ = UNI_REPLACEMENT_CHAR;
}
} else {
*target++ = (UTF16)ch; /* normal case */
}
} else if (ch > UNI_MAX_UTF16) {
if (flags == strictConversion) {
result = sourceIllegal;
source -= (extraBytesToRead+1); /* return to the start */
break; /* Bail out; shouldn't continue */
} else {
*target++ = UNI_REPLACEMENT_CHAR;
}
} else {
/* target is a character in range 0xFFFF - 0x10FFFF. */
if (target + 1 >= targetEnd) {
source -= (extraBytesToRead+1); /* Back up source pointer! */
result = targetExhausted; break;
}
ch -= halfBase;
*target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START);
*target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START);
}
}
*sourceStart = source;
*targetStart = target;
return result;
}
/* --------------------------------------------------------------------- */
ConversionResult ConvertUTF32toUTF8 (
const UTF32** sourceStart, const UTF32* sourceEnd,
UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) {
ConversionResult result = conversionOK;
const UTF32* source = *sourceStart;
UTF8* target = *targetStart;
while (source < sourceEnd) {
UTF32 ch;
unsigned short bytesToWrite = 0;
const UTF32 byteMask = 0xBF;
const UTF32 byteMark = 0x80;
ch = *source++;
if (flags == strictConversion ) {
/* UTF-16 surrogate values are illegal in UTF-32 */
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
--source; /* return to the illegal value itself */
result = sourceIllegal;
break;
}
}
/*
* Figure out how many bytes the result will require. Turn any
* illegally large UTF32 things (> Plane 17) into replacement chars.
*/
if (ch < (UTF32)0x80) { bytesToWrite = 1;
} else if (ch < (UTF32)0x800) { bytesToWrite = 2;
} else if (ch < (UTF32)0x10000) { bytesToWrite = 3;
} else if (ch <= UNI_MAX_LEGAL_UTF32) { bytesToWrite = 4;
} else { bytesToWrite = 3;
ch = UNI_REPLACEMENT_CHAR;
result = sourceIllegal;
}
target += bytesToWrite;
if (target > targetEnd) {
--source; /* Back up source pointer! */
target -= bytesToWrite; result = targetExhausted; break;
}
switch (bytesToWrite) { /* note: everything falls through. */
case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
case 1: *--target = (UTF8) (ch | firstByteMark[bytesToWrite]);
}
target += bytesToWrite;
}
*sourceStart = source;
*targetStart = target;
return result;
}
/* --------------------------------------------------------------------- */
ConversionResult ConvertUTF8toUTF32 (
const UTF8** sourceStart, const UTF8* sourceEnd,
UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags) {
ConversionResult result = conversionOK;
const UTF8* source = *sourceStart;
UTF32* target = *targetStart;
while (source < sourceEnd) {
UTF32 ch = 0;
unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
if (source + extraBytesToRead >= sourceEnd) {
result = sourceExhausted; break;
}
/* Do this check whether lenient or strict */
if (! isLegalUTF8(source, extraBytesToRead+1)) {
result = sourceIllegal;
break;
}
/*
* The cases all fall through. See "Note A" below.
*/
switch (extraBytesToRead) {
case 5: ch += *source++; ch <<= 6;
case 4: ch += *source++; ch <<= 6;
case 3: ch += *source++; ch <<= 6;
case 2: ch += *source++; ch <<= 6;
case 1: ch += *source++; ch <<= 6;
case 0: ch += *source++;
}
ch -= offsetsFromUTF8[extraBytesToRead];
if (target >= targetEnd) {
source -= (extraBytesToRead+1); /* Back up the source pointer! */
result = targetExhausted; break;
}
if (ch <= UNI_MAX_LEGAL_UTF32) {
/*
* UTF-16 surrogate values are illegal in UTF-32, and anything
* over Plane 17 (> 0x10FFFF) is illegal.
*/
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
if (flags == strictConversion) {
source -= (extraBytesToRead+1); /* return to the illegal value itself */
result = sourceIllegal;
break;
} else {
*target++ = UNI_REPLACEMENT_CHAR;
}
} else {
*target++ = ch;
}
} else { /* i.e., ch > UNI_MAX_LEGAL_UTF32 */
result = sourceIllegal;
*target++ = UNI_REPLACEMENT_CHAR;
}
}
*sourceStart = source;
*targetStart = target;
return result;
}
UTF32 *MakeUTF32FromUTF8(UTF8 *string)
{
UTF32 *ret, *tstart;
const UTF8 *tstring = string;
size_t string_length = strlen((char *)string);
tstart = ret = (UTF32 *)malloc(string_length * sizeof(UTF32) + 1);
ConvertUTF8toUTF32(&tstring, &string[string_length], &tstart, &tstart[string_length], lenientConversion);
*tstart = 0;
return(ret);
}
/* ---------------------------------------------------------------------
Note A.
The fall-through switches in UTF-8 reading code save a
temp variable, some decrements & conditionals. The switches
are equivalent to the following loop:
{
int tmpBytesToRead = extraBytesToRead+1;
do {
ch += *source++;
--tmpBytesToRead;
if (tmpBytesToRead) ch <<= 6;
} while (tmpBytesToRead > 0);
}
In UTF-8 writing code, the switches on "bytesToWrite" are
similarly unrolled loops.
--------------------------------------------------------------------- */

View File

@ -0,0 +1,149 @@
/*
* Copyright 2001-2004 Unicode, Inc.
*
* Disclaimer
*
* This source code is provided as is by Unicode, Inc. No claims are
* made as to fitness for any particular purpose. No warranties of any
* kind are expressed or implied. The recipient agrees to determine
* applicability of information provided. If this file has been
* purchased on magnetic or optical media from Unicode, Inc., the
* sole remedy for any claim will be exchange of defective media
* within 90 days of receipt.
*
* Limitations on Rights to Redistribute This Code
*
* Unicode, Inc. hereby grants the right to freely use the information
* supplied in this file in the creation of products supporting the
* Unicode Standard, and to make copies of this file in any form
* for internal or external distribution as long as this notice
* remains attached.
*/
/* ---------------------------------------------------------------------
Conversions between UTF32, UTF-16, and UTF-8. Header file.
Several funtions are included here, forming a complete set of
conversions between the three formats. UTF-7 is not included
here, but is handled in a separate source file.
Each of these routines takes pointers to input buffers and output
buffers. The input buffers are const.
Each routine converts the text between *sourceStart and sourceEnd,
putting the result into the buffer between *targetStart and
targetEnd. Note: the end pointers are *after* the last item: e.g.
*(sourceEnd - 1) is the last item.
The return result indicates whether the conversion was successful,
and if not, whether the problem was in the source or target buffers.
(Only the first encountered problem is indicated.)
After the conversion, *sourceStart and *targetStart are both
updated to point to the end of last text successfully converted in
the respective buffers.
Input parameters:
sourceStart - pointer to a pointer to the source buffer.
The contents of this are modified on return so that
it points at the next thing to be converted.
targetStart - similarly, pointer to pointer to the target buffer.
sourceEnd, targetEnd - respectively pointers to the ends of the
two buffers, for overflow checking only.
These conversion functions take a ConversionFlags argument. When this
flag is set to strict, both irregular sequences and isolated surrogates
will cause an error. When the flag is set to lenient, both irregular
sequences and isolated surrogates are converted.
Whether the flag is strict or lenient, all illegal sequences will cause
an error return. This includes sequences such as: <F4 90 80 80>, <C0 80>,
or <A0> in UTF-8, and values above 0x10FFFF in UTF-32. Conformant code
must check for illegal sequences.
When the flag is set to lenient, characters over 0x10FFFF are converted
to the replacement character; otherwise (when the flag is set to strict)
they constitute an error.
Output parameters:
The value "sourceIllegal" is returned from some routines if the input
sequence is malformed. When "sourceIllegal" is returned, the source
value will point to the illegal value that caused the problem. E.g.,
in UTF-8 when a sequence is malformed, it points to the start of the
malformed sequence.
Author: Mark E. Davis, 1994.
Rev History: Rick McGowan, fixes & updates May 2001.
Fixes & updates, Sept 2001.
------------------------------------------------------------------------ */
/* ---------------------------------------------------------------------
The following 4 definitions are compiler-specific.
The C standard does not guarantee that wchar_t has at least
16 bits, so wchar_t is no less portable than unsigned short!
All should be unsigned values to avoid sign extension during
bit mask & shift operations.
------------------------------------------------------------------------ */
/* Some fundamental constants */
#define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD
#define UNI_MAX_BMP (UTF32)0x0000FFFF
#define UNI_MAX_UTF16 (UTF32)0x0010FFFF
#define UNI_MAX_UTF32 (UTF32)0x7FFFFFFF
#define UNI_MAX_LEGAL_UTF32 (UTF32)0x0010FFFF
typedef enum {
conversionOK, /* conversion successful */
sourceExhausted, /* partial character in source, but hit end */
targetExhausted, /* insuff. room in target for conversion */
sourceIllegal /* source sequence is illegal/malformed */
} ConversionResult;
typedef enum {
strictConversion = 0,
lenientConversion
} ConversionFlags;
/* This is for C++ and does no harm in C */
#ifdef __cplusplus
extern "C" {
#endif
ConversionResult ConvertUTF8toUTF16 (
const UTF8** sourceStart, const UTF8* sourceEnd,
UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags);
ConversionResult ConvertUTF16toUTF8 (
const UTF16** sourceStart, const UTF16* sourceEnd,
UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags);
ConversionResult ConvertUTF8toUTF32 (
const UTF8** sourceStart, const UTF8* sourceEnd,
UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags);
ConversionResult ConvertUTF32toUTF8 (
const UTF32** sourceStart, const UTF32* sourceEnd,
UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags);
ConversionResult ConvertUTF16toUTF32 (
const UTF16** sourceStart, const UTF16* sourceEnd,
UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags);
ConversionResult ConvertUTF32toUTF16 (
const UTF32** sourceStart, const UTF32* sourceEnd,
UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags);
bool isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd);
/* Extra Mednafen convenience functions. */
UTF32 *MakeUTF32FromUTF8(UTF8 *string);
#ifdef __cplusplus
}
#endif
/* --------------------------------------------------------------------- */

View File

@ -0,0 +1,2 @@
mednafen_SOURCES += string/escape.cpp string/trim.cpp string/ConvertUTF.cpp

View File

@ -0,0 +1,157 @@
/* Mednafen - Multi-system Emulator
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "../mednafen.h"
#include "escape.h"
static unsigned int hex_nibble_to_val(char nibble)
{
unsigned int ret = 0;
nibble = tolower(nibble);
if(nibble >= '0' && nibble <= '9')
ret = nibble - '0';
else
ret = nibble - 'a';
return(ret);
}
void unescape_string(char *string)
{
char *src = string;
bool inescape = 0;
uint8 hoval = 0;
int inhex = 0;
int inoctal = 0;
while(*src)
{
if(*src == '\\')
{
inescape = TRUE;
inhex = 0;
inoctal = 0;
}
else if(inhex)
{
if(inhex == 1)
{
hoval = hex_nibble_to_val(*src) << 4;
inhex++;
}
else if(inhex == 2)
{
hoval |= hex_nibble_to_val(*src);
*string = hoval;
string++;
hoval = 0;
inhex = 0;
}
}
else if(inoctal)
{
if(inoctal == 1)
{
hoval = (*src - '0') * 8 * 8;
}
else if(inoctal == 2)
{
hoval += (*src - '0') * 8;
}
else
{
hoval += *src - '0';
*string = hoval;
string++;
hoval = 0;
inoctal = 0;
}
}
else if(inescape)
{
switch(*src)
{
case 'a': *string = 7; string++; break;
case 'b': *string = 8; string++; break;
case 'f': *string = 12; string++; break;
case 'n': *string = 10; string++; break;
case 'r': *string = 13; string++; break;
case 't': *string = 9; string++; break;
case 'v': *string = 11; string++; break;
case '\\': *string = '\\'; string++; break;
case '?': *string = '?'; string++; break;
case '\'': *string = '\''; string++; break;
case '"': *string = '"'; string++; break;
case 'o': inoctal = 1; break;
case 'x': inhex = 1; break;
default: *string = *src; string++; break;
}
inescape = 0;
}
else
{
*string = *src;
string++;
}
src++;
}
*string = 0;
}
char *escape_string(const char *text)
{
uint32 slen = strlen(text);
char *ret = (char*)malloc(slen * 4 + 1); // \xFF
char *outoo = ret;
for(uint32 x = 0; x < slen; x++)
{
int c = (uint8)text[x];
if(c < 0x20 || c == 0x7F || c == '\\' || c == '\'' || c == '"')
{
*outoo++ = '\\';
switch(c)
{
case '\\': *outoo++ = '\\'; break;
case '\'': *outoo++ = '\''; break;
case '"': *outoo++ = '"'; break;
case 7: *outoo++ = 'a'; break;
case 8: *outoo++ = 'b'; break;
case 12: *outoo++ = 'f'; break;
case 10: *outoo++ = 'n'; break;
case 13: *outoo++ = 'r'; break;
case 9: *outoo++ = 't'; break;
case 11: *outoo++ = 'v'; break;
default: outoo += sprintf(outoo, "x%02x", c); break;
}
}
else
*outoo++ = c;
}
*outoo = 0;
return(ret);
}

View File

@ -0,0 +1,9 @@
#ifndef __MDFN_ESCAPE_H
#define __MDFN_ESCAPE_H
// These functions are safe to call before calling MDFNI_Initialize().
void unescape_string(char *string);
char* escape_string(const char *text);
#endif

View File

@ -0,0 +1,147 @@
/* Mednafen - Multi-system Emulator
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <string>
#include "emuware/emuware.h"
#include "trim.h"
// Remove whitespace from beginning of string
void MDFN_ltrim(char *string)
{
int32 di, si;
bool InWhitespace = true;
di = si = 0;
while(string[si])
{
if(InWhitespace && (string[si] == ' ' || string[si] == '\r' || string[si] == '\n' || string[si] == '\t' || string[si] == 0x0b))
{
}
else
{
InWhitespace = false;
string[di] = string[si];
di++;
}
si++;
}
string[di] = 0;
}
// Remove whitespace from end of string
void MDFN_rtrim(char *string)
{
int32 len = strlen(string);
if(len)
{
for(int32 x = len - 1; x >= 0; x--)
{
if(string[x] == ' ' || string[x] == '\r' || string[x] == '\n' || string[x] == '\t' || string[x] == 0x0b)
string[x] = 0;
else
break;
}
}
}
void MDFN_trim(char *string)
{
MDFN_rtrim(string);
MDFN_ltrim(string);
}
// Remove whitespace from beginning of string
void MDFN_ltrim(std::string &string)
{
size_t len = string.length();
size_t di, si;
bool InWhitespace = true;
di = si = 0;
while(si < len)
{
if(InWhitespace && (string[si] == ' ' || string[si] == '\r' || string[si] == '\n' || string[si] == '\t' || string[si] == 0x0b))
{
}
else
{
InWhitespace = false;
string[di] = string[si];
di++;
}
si++;
}
string.resize(di);
}
// Remove whitespace from end of string
void MDFN_rtrim(std::string &string)
{
size_t len = string.length();
if(len)
{
size_t x = len;
size_t new_len = len;
do
{
x--;
if(!(string[x] == ' ' || string[x] == '\r' || string[x] == '\n' || string[x] == '\t' || string[x] == 0x0b))
break;
new_len--;
} while(x);
string.resize(new_len);
}
}
void MDFN_trim(std::string &string)
{
MDFN_rtrim(string);
MDFN_ltrim(string);
}
char *MDFN_RemoveControlChars(char *str)
{
char *orig = str;
if(str)
{
while(*str)
{
if((unsigned char)*str < 0x20)
*str = 0x20;
str++;
}
}
return(orig);
}

View File

@ -0,0 +1,14 @@
#ifndef __MDFN_STRING_TRIM_H
#define __MDFN_STRING_TRIM_H
void MDFN_ltrim(char *string);
void MDFN_rtrim(char *string);
void MDFN_trim(char *string);
void MDFN_ltrim(std::string &string);
void MDFN_rtrim(std::string &string);
void MDFN_trim(std::string &string);
char *MDFN_RemoveControlChars(char *str);
#endif