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:
parent
313c0a1c27
commit
e80de55210
|
@ -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>
|
||||
|
|
|
@ -201,6 +201,8 @@ namespace BizHawk.Client.DiscoHawk
|
|||
else
|
||||
{
|
||||
//test stuff...
|
||||
MednadiscTester tester = new MednadiscTester();
|
||||
tester.TestDirectory(@"c:\isos\psx");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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>
|
|
@ -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
|
|
@ -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>
|
|
@ -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>
|
|
@ -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
|
||||
}
|
|
@ -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
|
|
@ -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)
|
||||
{
|
||||
|
||||
}
|
||||
|
|
@ -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
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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];
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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));
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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 */
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
@ -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
|
|
@ -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)
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,3 @@
|
|||
#ifndef __GNUC__
|
||||
#pragma pack(pop)
|
||||
#endif
|
|
@ -0,0 +1,3 @@
|
|||
#include "emuware.h"
|
||||
|
||||
//this file intentionally empty
|
|
@ -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
|
|
@ -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.
|
||||
------------------------------------------------------------------------
|
|
@ -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_ ]
|
|
@ -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_ ]
|
|
@ -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
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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 = "";
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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.
|
||||
|
||||
--------------------------------------------------------------------- */
|
|
@ -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
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
|
@ -0,0 +1,2 @@
|
|||
mednafen_SOURCES += string/escape.cpp string/trim.cpp string/ConvertUTF.cpp
|
||||
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
Loading…
Reference in New Issue