Update to new cdrom code

This commit is contained in:
twinaphex 2016-08-14 12:12:16 +02:00
parent cb94564349
commit 69eef0ee17
41 changed files with 6555 additions and 2702 deletions

View File

@ -5,6 +5,7 @@ DEPS_DIR := $(CORE_DIR)/deps
LIBRETRO_DIR := $(CORE_DIR)/libretro-common
MEDNAFEN_DIR := $(CORE_DIR)/mednafen
CORE_EMU_DIR := $(MEDNAFEN_DIR)/ss
CDROM_DIR := $(MEDNAFEN_DIR)/cdrom
INCFLAGS := -I$(CORE_DIR) -I$(MEDNAFEN_DIR) -I$(MEDNAFEN_DIR)/include -I$(MEDNAFEN_DIR)/intl -I$(MEDNAFEN_DIR)/hw_sound -I$(MEDNAFEN_DIR)/hw_cpu -I$(MEDNAFEN_DIR)/hw_misc -I$(LIBRETRO_DIR)/include -I$(DEPS_DIR)/zlib
@ -109,22 +110,21 @@ ifeq ($(NEED_TREMOR), 1)
SOURCES_C += $(wildcard $(MEDNAFEN_DIR)/tremor/*.c)
endif
ifeq ($(NEED_CD), 1)
SOURCES_CXX += $(MEDNAFEN_DIR)/cdrom/CDAccess.cpp \
$(MEDNAFEN_DIR)/cdrom/CDAccess_Image.cpp \
$(MEDNAFEN_DIR)/cdrom/CDAccess_CCD.cpp \
$(MEDNAFEN_DIR)/cdrom/audioreader.cpp \
$(MEDNAFEN_DIR)/cdrom/misc.cpp \
$(MEDNAFEN_DIR)/cdrom/cdromif.cpp
ifneq ($(HAVE_GRIFFIN),1)
SOURCES_CXX += $(CDROM_DIR)/CDAccess.cpp \
$(CDROM_DIR)/CDAccess_Image.cpp \
$(CDROM_DIR)/CDAccess_CCD.cpp \
$(CDROM_DIR)/CDAFReader.cpp \
$(CDROM_DIR)/CDAFReader_Vorbis.cpp \
$(CDROM_DIR)/cdromif.cpp \
$(CDROM_DIR)/CDUtility.cpp \
$(CDROM_DIR)/lec.cpp \
$(CDROM_DIR)/galois.cpp \
$(CDROM_DIR)/recover-raw.cpp \
$(CDROM_DIR)/l-ec.cpp \
$(CDROM_DIR)/edc_crc32.cpp
endif
SOURCES_C += \
$(MEDNAFEN_DIR)/cdrom/CDUtility.c \
$(MEDNAFEN_DIR)/cdrom/galois.c \
$(MEDNAFEN_DIR)/cdrom/l-ec.c \
$(MEDNAFEN_DIR)/cdrom/lec.c \
$(MEDNAFEN_DIR)/cdrom/recover-raw.c \
$(MEDNAFEN_DIR)/cdrom/edc_crc32.c
FLAGS += -DNEED_CD
SOURCES_CXX += \
$(MEDNAFEN_DIR)/error.cpp \

View File

@ -1371,7 +1371,6 @@ static bool Load(MDFNFILE* fp)
unsigned i;
if(MDFN_GetSettingS("ss.dbg_exe_cdpath") != "")
{
bool success;
RMD_Drive dr;
dr.Name = std::string("Virtual CD Drive");
@ -1387,8 +1386,7 @@ static bool Load(MDFNFILE* fp)
static std::vector<CDIF *> CDInterfaces;
CDInterfaces.clear();
CDInterfaces.push_back(CDIF_Open(&success, MDFN_GetSettingS("ss.dbg_exe_cdpath").c_str(), false,
false));
CDInterfaces.push_back(CDIF_Open(MDFN_GetSettingS("ss.dbg_exe_cdpath").c_str(), false));
cdifs = &CDInterfaces;
}
@ -2250,14 +2248,14 @@ static MDFNGI *MDFNI_LoadCD(const char *devicename)
for(unsigned i = 0; i < file_list.size(); i++)
{
bool success = true;
CDIF *image = CDIF_Open(&success, file_list[i].c_str(), false, old_cdimagecache);
CDIF *image = CDIF_Open(file_list[i].c_str(), false);
CDInterfaces.push_back(image);
}
}
else
{
bool success = true;
CDIF *image = CDIF_Open(&success, devicename, false, old_cdimagecache);
CDIF *image = CDIF_Open(devicename, false);
log_cb(RETRO_LOG_INFO, "Pushing CD image onto stack: %s.\n", devicename);
CDInterfaces.push_back(image);
}
@ -2272,7 +2270,6 @@ static MDFNGI *MDFNI_LoadCD(const char *devicename)
for(unsigned i = 0; i < CDInterfaces.size(); i++)
{
TOC toc;
TOC_Clear(&toc);
CDInterfaces[i]->ReadTOC(&toc);
@ -2296,9 +2293,8 @@ static MDFNGI *MDFNI_LoadCD(const char *devicename)
for(unsigned i = 0; i < CDInterfaces.size(); i++)
{
CD_TOC toc;
TOC toc;
TOC_Clear(&toc);
CDInterfaces[i]->ReadTOC(&toc);
layout_md5.update_u32_as_lsb(toc.first_track);

View File

@ -0,0 +1,60 @@
/******************************************************************************/
/* Mednafen - Multi-system Emulator */
/******************************************************************************/
/* CDAFReader.cpp:
** Copyright (C) 2010-2016 Mednafen Team
**
** 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.,
** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
// CDAFR_Open(), and CDAFReader, 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 CDAFReader 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/mednafen.h>
#include "CDAFReader.h"
#include "CDAFReader_Vorbis.h"
#include "CDAFReader_MPC.h"
CDAFReader::CDAFReader() : LastReadPos(0)
{
}
CDAFReader::~CDAFReader()
{
}
CDAFReader *CDAFR_Open(Stream *fp)
{
static CDAFReader* (* const OpenFuncs[])(Stream* fp) =
{
#ifdef HAVE_MPC
CDAFR_MPC_Open,
#endif
CDAFR_Vorbis_Open
};
for(auto const& f : OpenFuncs)
{
return f(fp);
}
return(NULL);
}

View File

@ -0,0 +1,62 @@
/******************************************************************************/
/* Mednafen - Multi-system Emulator */
/******************************************************************************/
/* CDAFReader.h:
** Copyright (C) 2010-2016 Mednafen Team
**
** 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.,
** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __MDFN_CDAFREADER_H
#define __MDFN_CDAFREADER_H
#include <mednafen/Stream.h>
class CDAFReader
{
public:
CDAFReader();
virtual ~CDAFReader();
virtual uint64_t FrameCount(void) = 0;
INLINE uint64_t Read(uint64_t frame_offset, int16 *buffer, uint64_t frames)
{
uint64_t ret;
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 uint64_t Read_(int16 *buffer, uint64_t frames) = 0;
virtual bool Seek_(uint64_t frame_offset) = 0;
uint64_t LastReadPos;
};
// AR_Open(), and CDAFReader, 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 CDAFReader object exists.
CDAFReader *CDAFR_Open(Stream *fp);
#endif

View File

@ -0,0 +1,203 @@
/******************************************************************************/
/* Mednafen - Multi-system Emulator */
/******************************************************************************/
/* CDAFReader_MPC.cpp:
** Copyright (C) 2006-2016 Mednafen Team
**
** 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.,
** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <mednafen/mednafen.h>
#include "CDAFReader.h"
#include "CDAFReader_MPC.h"
#include <mednafen/mpcdec/mpcdec.h>
class CDAFReader_MPC final : public CDAFReader
{
public:
CDAFReader_MPC(Stream *fp);
~CDAFReader_MPC();
uint64_t Read_(int16 *buffer, uint64_t frames) override;
bool Seek_(uint64_t frame_offset) override;
uint64_t FrameCount(void) override;
private:
mpc_reader reader;
mpc_demux *demux;
mpc_streaminfo si;
MPC_SAMPLE_FORMAT MPCBuffer[MPC_DECODER_BUFFER_LENGTH];
uint32_t MPCBufferIn;
uint32_t MPCBufferOffs;
Stream *fw;
};
/// Reads size bytes of data into buffer at ptr.
static mpc_int32_t_t impc_read(mpc_reader *p_reader, void *ptr, mpc_int32_t_t size)
{
Stream *fw = (Stream*)(p_reader->data);
return fw->read(ptr, size, false);
}
/// Seeks to byte position offset.
static mpc_bool_t impc_seek(mpc_reader *p_reader, mpc_int32_t_t offset)
{
Stream *fw = (Stream*)(p_reader->data);
fw->seek(offset, SEEK_SET);
return(MPC_TRUE);
}
/// Returns the current byte offset in the stream.
static mpc_int32_t_t impc_tell(mpc_reader *p_reader)
{
Stream *fw = (Stream*)(p_reader->data);
return fw->tell();
}
/// Returns the total length of the source stream, in bytes.
static mpc_int32_t_t impc_get_size(mpc_reader *p_reader)
{
Stream *fw = (Stream*)(p_reader->data);
return fw->size();
}
/// True if the stream is a seekable stream.
static mpc_bool_t impc_canseek(mpc_reader *p_reader)
{
return(MPC_TRUE);
}
CDAFReader_MPC::CDAFReader_MPC(Stream *fp) : fw(fp)
{
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);
}
}
CDAFReader_MPC::~CDAFReader_MPC()
{
if(demux)
{
mpc_demux_exit(demux);
demux = NULL;
}
}
uint64_t CDAFReader_MPC::Read_(int16 *buffer, uint64_t frames)
{
mpc_status err;
int16 *cowbuf = (int16 *)buffer;
int32_t toread = frames * 2;
while(toread > 0)
{
int32_t 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++)
{
int32_t samp = MPCBuffer[MPCBufferOffs + x] >> MPC_FIXED_POINT_FRACTPART;
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 CDAFReader_MPC::Seek_(uint64_t frame_offset)
{
MPCBufferOffs = 0;
MPCBufferIn = 0;
if(mpc_demux_seek_sample(demux, frame_offset) < 0)
return(false);
return(true);
}
uint64_t CDAFReader_MPC::FrameCount(void)
{
return(mpc_streaminfo_get_length_samples(&si));
}
CDAFReader* CDAFR_MPC_Open(Stream* fp)
{
return new CDAFReader_MPC(fp);
}

View File

@ -0,0 +1,27 @@
/******************************************************************************/
/* Mednafen - Multi-system Emulator */
/******************************************************************************/
/* CDAFReader_MPC.h:
** Copyright (C) 2015-2016 Mednafen Team
**
** 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.,
** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __MDFN_CDAFREADER_MPC_H
#define __MDFN_CDAFREADER_MPC_H
CDAFReader* CDAFR_MPC_Open(Stream* fp);
#endif

View File

@ -0,0 +1,129 @@
/******************************************************************************/
/* Mednafen - Multi-system Emulator */
/******************************************************************************/
/* CDAFReader_Vorbis.cpp:
** Copyright (C) 2010-2016 Mednafen Team
**
** 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.,
** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <mednafen/mednafen.h>
#include "CDAFReader.h"
#include "CDAFReader_Vorbis.h"
#include <mednafen/tremor/ivorbisfile.h>
class CDAFReader_Vorbis final : public CDAFReader
{
public:
CDAFReader_Vorbis(Stream *fp);
~CDAFReader_Vorbis();
uint64_t Read_(int16_t *buffer, uint64_t frames) override;
bool Seek_(uint64_t frame_offset) override;
uint64_t FrameCount(void) override;
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);
return fw->read(ptr, size * nmemb, false) / size;
}
static int iov_seek_func(void *user_data, int64_t offset, int whence)
{
Stream *fw = (Stream*)user_data;
fw->seek(offset, whence);
return(0);
}
static int iov_close_func(void *user_data)
{
Stream *fw = (Stream*)user_data;
fw->close();
return(0);
}
static long iov_tell_func(void *user_data)
{
Stream *fw = (Stream*)user_data;
return fw->tell();
}
CDAFReader_Vorbis::CDAFReader_Vorbis(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;
if(ov_open_callbacks(fp, &ovfile, NULL, 0, cb))
throw(0);
}
CDAFReader_Vorbis::~CDAFReader_Vorbis()
{
ov_clear(&ovfile);
}
uint64_t CDAFReader_Vorbis::Read_(int16_t *buffer, uint64_t frames)
{
uint8 *tw_buf = (uint8 *)buffer;
int cursection = 0;
long toread = frames * sizeof(int16_t) * 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_t) / 2);
}
bool CDAFReader_Vorbis::Seek_(uint64_t frame_offset)
{
ov_pcm_seek(&ovfile, frame_offset);
return(true);
}
uint64_t CDAFReader_Vorbis::FrameCount(void)
{
return(ov_pcm_total(&ovfile, -1));
}
CDAFReader* CDAFR_Vorbis_Open(Stream* fp)
{
return new CDAFReader_Vorbis(fp);
}

View File

@ -0,0 +1,28 @@
/******************************************************************************/
/* Mednafen - Multi-system Emulator */
/******************************************************************************/
/* CDAFReader_Vorbis.h:
** Copyright (C) 2015-2016 Mednafen Team
**
** 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.,
** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __MDFN_CDAFREADER_VORBIS_H
#define __MDFN_CDAFREADER_VORBIS_H
CDAFReader* CDAFR_Vorbis_Open(Stream* fp);
#endif

View File

@ -15,16 +15,7 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <sys/stat.h>
#ifdef _WIN32
#include <direct.h>
#else
#include <unistd.h>
#endif
#include "../mednafen.h"
#include "CDAccess.h"
#include "CDAccess_Image.h"
#include "CDAccess_CCD.h"
@ -39,9 +30,15 @@ CDAccess::~CDAccess()
}
CDAccess *cdaccess_open_image(bool *success, const char *path, bool image_memcache)
CDAccess* CDAccess_Open(const std::string& path, bool image_memcache)
{
if(strlen(path) >= 4 && !strcasecmp(path + strlen(path) - 4, ".ccd"))
return new CDAccess_CCD(success, path, image_memcache);
return new CDAccess_Image(success, path, 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;
}

View File

@ -2,12 +2,8 @@
#define __MDFN_CDROMFILE_H
#include <stdio.h>
#include <stdint.h>
#include <boolean.h>
#include "CDUtility.h"
#include "misc.h"
class CDAccess
{
@ -16,17 +12,21 @@ class CDAccess
CDAccess();
virtual ~CDAccess();
virtual bool Read_Raw_Sector(uint8_t *buf, int32_t lba) = 0;
virtual void Read_Raw_Sector(uint8_t *buf, int32_t lba) = 0;
virtual bool Read_TOC(TOC *toc) = 0;
// Returns false if the read wouldn't be "fast"(i.e. reading from a disk),
// or if the read can't be done in a thread-safe re-entrant manner.
//
// Writes 96 bytes into pwbuf, and returns 'true' otherwise.
virtual bool Fast_Read_Raw_PW_TSRE(uint8_t* pwbuf, int32_t lba) const noexcept = 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
virtual void Read_TOC(TOC *toc) = 0;
private:
CDAccess(const CDAccess&); // No copy constructor.
CDAccess& operator=(const CDAccess&); // No assignment operator.
};
CDAccess *cdaccess_open_image(bool *success, const char *path, bool image_memcache);
CDAccess* CDAccess_Open(const std::string& path, bool image_memcache);
#endif

View File

@ -1,50 +1,63 @@
/* 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
*/
/******************************************************************************/
/* Mednafen - Multi-system Emulator */
/******************************************************************************/
/* CDAccess_CCD.cpp:
** Copyright (C) 2013-2016 Mednafen Team
**
** 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.,
** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <mednafen/mednafen.h>
#include <mednafen/general.h>
#include "../mednafen.h"
#include "../general.h"
#include <compat/msvc.h>
#include "CDAccess_CCD.h"
#include "CDUtility.h"
#include <limits>
#include <limits.h>
#include <map>
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>
template<typename T>
static T CCD_ReadInt(CCD_Section &s, const std::string &propname, const bool have_defval = false, const int defval = 0)
{
const char *vp;
char *ep = NULL;
int scan_base = 10;
size_t scan_offset = 0;
long ret = 0;
CCD_Section::iterator zit = s.find(propname);
if(zit == s.end())
{
if(have_defval)
return defval;
throw MDFN_Error(0, _("Missing property: %s"), propname.c_str());
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')
{
@ -52,7 +65,8 @@ static T CCD_ReadInt(CCD_Section &s, const std::string &propname, const bool hav
scan_offset = 2;
}
vp = v.c_str() + scan_offset;
const char *vp = v.c_str() + scan_offset;
char *ep = NULL;
if(std::numeric_limits<T>::is_signed)
ret = strtol(vp, &ep, scan_base);
@ -60,22 +74,22 @@ static T CCD_ReadInt(CCD_Section &s, const std::string &propname, const bool hav
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());
}
return ret;
}
CDAccess_CCD::CDAccess_CCD(bool *success, const char *path, bool image_memcache) : img_stream(NULL), sub_stream(NULL), img_numsectors(0)
CDAccess_CCD::CDAccess_CCD(const std::string& path, bool image_memcache) : img_numsectors(0)
{
TOC_Clear(&tocd);
if (!Load(path, image_memcache))
*success = false;
Load(path, image_memcache);
}
bool CDAccess_CCD::Load(const char *path, bool image_memcache)
bool CDAccess_CCD::Load(const std::string& path, bool image_memcache)
{
FileStream cf(path, MODE_READ);
FileStream cf(path.c_str(), MODE_READ);
std::map<std::string, CCD_Section> Sections;
std::string linebuf;
std::string cur_section_name;
@ -87,11 +101,9 @@ bool CDAccess_CCD::Load(const char *path, bool image_memcache)
if(file_ext.length() == 4 && file_ext[0] == '.')
{
int i;
signed char av = -1;
signed char extupt[3] = { -1, -1, -1 };
for(i = 1; i < 4; i++)
for(int i = 1; i < 4; i++)
{
if(file_ext[i] >= 'A' && file_ext[i] <= 'Z')
extupt[i - 1] = 'A' - 'a';
@ -99,7 +111,8 @@ bool CDAccess_CCD::Load(const char *path, bool image_memcache)
extupt[i - 1] = 0;
}
for(i = 0; i < 3; i++)
signed char av = -1;
for(int i = 0; i < 3; i++)
{
if(extupt[i] != -1)
av = extupt[i];
@ -110,13 +123,13 @@ bool CDAccess_CCD::Load(const char *path, bool image_memcache)
if(av == -1)
av = 0;
for(i = 0; i < 3; i++)
for(int i = 0; i < 3; i++)
{
if(extupt[i] == -1)
extupt[i] = av;
}
for(i = 0; i < 3; i++)
for(int i = 0; i < 3; i++)
{
img_extsd[i] += extupt[i];
sub_extsd[i] += extupt[i];
@ -129,7 +142,8 @@ bool CDAccess_CCD::Load(const char *path, bool image_memcache)
while(cf.get_line(linebuf) >= 0)
{
MDFN_trim(linebuf);
MDFN_rtrim(linebuf);
MDFN_ltrim(linebuf);
if(linebuf.length() == 0) // Skip blank lines.
continue;
@ -138,7 +152,7 @@ bool CDAccess_CCD::Load(const char *path, bool image_memcache)
{
if(linebuf.length() < 3 || linebuf[linebuf.length() - 1] != ']')
{
MDFN_Error(0, _("Malformed section specifier: %s"), linebuf.c_str());
log_cb(RETRO_LOG_ERROR, "Malformed section specifier: %s", linebuf.c_str());
return false;
}
@ -147,21 +161,24 @@ bool CDAccess_CCD::Load(const char *path, bool image_memcache)
}
else
{
std::string k, v;
const size_t feqpos = linebuf.find('=');
const size_t leqpos = linebuf.rfind('=');
std::string k, v;
if(feqpos == std::string::npos || feqpos != leqpos)
{
MDFN_Error(0, _("Malformed value pair specifier: %s"), linebuf.c_str());
log_cb(RETRO_LOG_ERROR, "Malformed value pair specifier: %s\n", linebuf.c_str());
return false;
}
k = linebuf.substr(0, feqpos);
v = linebuf.substr(feqpos + 1);
MDFN_trim(k);
MDFN_trim(v);
MDFN_rtrim(k);
MDFN_ltrim(k);
MDFN_rtrim(v);
MDFN_ltrim(v);
MDFN_strtoupper(k);
@ -170,117 +187,116 @@ bool CDAccess_CCD::Load(const char *path, bool image_memcache)
}
{
unsigned te;
CCD_Section &ds = Sections["DISC"];
unsigned toc_entries = CCD_ReadInt<unsigned>(ds, "TOCENTRIES");
unsigned num_sessions = CCD_ReadInt<unsigned>(ds, "SESSIONS");
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)
{
MDFN_Error(0, _("Unsupported number of sessions: %u"), num_sessions);
log_cb(RETRO_LOG_ERROR, "Unsupported number of sessions: %u\n", num_sessions);
return false;
}
if(data_tracks_scrambled)
{
MDFN_Error(0, _("Scrambled CCD data tracks currently not supported."));
log_cb(RETRO_LOG_ERROR, "Scrambled CCD data tracks currently not supported.\n");
return false;
}
//printf("MOO: %d\n", toc_entries);
for(te = 0; te < toc_entries; te++)
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)];
CCD_Section& ts = Sections[std::string(tmpbuf)];
unsigned session = CCD_ReadInt<unsigned>(ts, "SESSION");
uint8_t point = CCD_ReadInt<uint8>(ts, "POINT");
uint8_t adr = CCD_ReadInt<uint8>(ts, "ADR");
uint8_t control = CCD_ReadInt<uint8>(ts, "CONTROL");
uint8_t pmin = CCD_ReadInt<uint8>(ts, "PMIN");
uint8_t psec = CCD_ReadInt<uint8>(ts, "PSEC");
uint8_t pframe = CCD_ReadInt<uint8>(ts, "PFRAME");
signed plba = CCD_ReadInt<signed>(ts, "PLBA");
uint8_t point = CCD_ReadInt<uint8_t>(ts, "POINT");
uint8_t adr = CCD_ReadInt<uint8_t>(ts, "ADR");
uint8_t control = CCD_ReadInt<uint8_t>(ts, "CONTROL");
uint8_t pmin = CCD_ReadInt<uint8_t>(ts, "PMIN");
uint8_t psec = CCD_ReadInt<uint8_t>(ts, "PSEC");
//uint8_t pframe = CCD_ReadInt<uint8_t>(ts, "PFRAME");
signed plba = CCD_ReadInt<signed>(ts, "PLBA");
if(session != 1)
{
MDFN_Error(0, "Unsupported TOC entry Session value: %u", session);
log_cb(RETRO_LOG_ERROR, "Unsupported TOC entry Session value: %u\n", session);
return false;
}
// Reference: ECMA-394, page 5-14
if (point >= 1 && point <= 99)
if(point >= 1 && point <= 99)
{
tocd.tracks[point].adr = adr;
tocd.tracks[point].control = control;
tocd.tracks[point].lba = plba;
tocd.tracks[point].valid = true;
}
else
switch(point)
{
default:
MDFN_Error(0, "Unsupported TOC entry Point value: %u", point);
return false;
case 0xA0:
tocd.first_track = pmin;
tocd.disc_type = psec;
break;
else switch(point)
{
default:
log_cb(RETRO_LOG_ERROR, "Unsupported TOC entry Point value: %u\n", point);
return false;
case 0xA0:
tocd.first_track = pmin;
tocd.disc_type = psec;
break;
case 0xA1:
tocd.last_track = pmin;
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;
}
case 0xA2:
tocd.tracks[100].adr = adr;
tocd.tracks[100].control = control;
tocd.tracks[100].lba = plba;
tocd.tracks[100].valid = true;
break;
}
}
}
/* Convenience leadout track duplication. */
if(tocd.last_track < 99)
tocd.tracks[tocd.last_track + 1] = tocd.tracks[100];
/* Open image stream. */
// Open image stream.
{
std::string image_path = MDFN_EvalFIP(dir_path, file_base + std::string(".") + std::string(img_extsd), true);
FileStream *str = new FileStream(image_path.c_str(), MODE_READ);
if(image_memcache)
img_stream = new MemoryStream(str);
img_stream = new MemoryStream(new FileStream(image_path.c_str(), MODE_READ));
else
img_stream = str;
img_stream = new FileStream(image_path.c_str(), MODE_READ);
int64 ss = img_stream->size();
uint64 ss = img_stream->size();
if(ss % 2352)
{
MDFN_Error(0, _("CCD image size is not evenly divisible by 2352."));
log_cb(RETRO_LOG_ERROR, "CCD image size is not evenly divisible by 2352.\n");
return false;
}
if(ss > 0x7FFFFFFF)
{
log_cb(RETRO_LOG_ERROR, "CCD image is too large.\n");
return false;
}
img_numsectors = ss / 2352;
}
// Open subchannel stream
{
/* Open subchannel stream */
std::string sub_path = MDFN_EvalFIP(dir_path, file_base + std::string(".") + std::string(sub_extsd), true);
FileStream *str = new FileStream(sub_path.c_str(), MODE_READ);
FileStream sub_stream(sub_path.c_str(), MODE_READ);
if(image_memcache)
sub_stream = new MemoryStream(str);
else
sub_stream = str;
if(sub_stream->size() != (int64)img_numsectors * 96)
if(sub_stream.size() != (uint64)img_numsectors * 96)
{
MDFN_Error(0, _("CCD SUB file size mismatch."));
log_cb(RETRO_LOG_ERROR, "CCD SUB file size mismatch.\n");
return false;
}
sub_data = new uint8_t[(uint64)img_numsectors * 96];
sub_stream.read(sub_data, (uint64)img_numsectors * 96);
}
CheckSubQSanity();
@ -298,128 +314,133 @@ bool CDAccess_CCD::Load(const char *path, bool image_memcache)
//
bool CDAccess_CCD::CheckSubQSanity(void)
{
size_t s;
size_t checksum_pass_counter = 0;
int prev_lba = INT_MAX;
uint8_t prev_track = 0;
int prev_lba = INT_MAX;
uint8_t prev_track = 0;
// Silence GCC warning
(void)prev_lba;
for(s = 0; s < img_numsectors; s++)
for(size_t s = 0; s < img_numsectors; s++)
{
uint8_t adr;
union
{
uint8 full[96];
uint8_t full[96];
struct
{
uint8 pbuf[12];
uint8 qbuf[12];
uint8_t pbuf[12];
uint8_t qbuf[12];
};
} buf;
sub_stream->seek(s * 96, SEEK_SET);
sub_stream->read(buf.full, 96);
memcpy(buf.full, &sub_data[s * 96], 96);
if(!subq_check_checksum(buf.qbuf))
continue;
adr = buf.qbuf[0] & 0xF;
if(adr == 0x01)
if(subq_check_checksum(buf.qbuf))
{
int lba;
uint8_t track;
uint8_t track_bcd = buf.qbuf[1];
uint8_t index_bcd = buf.qbuf[2];
uint8_t rm_bcd = buf.qbuf[3];
uint8_t rs_bcd = buf.qbuf[4];
uint8_t rf_bcd = buf.qbuf[5];
uint8_t am_bcd = buf.qbuf[7];
uint8_t as_bcd = buf.qbuf[8];
uint8_t af_bcd = buf.qbuf[9];
uint8_t adr = buf.qbuf[0] & 0xF;
//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)
if(adr == 0x01)
{
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);
return false;
uint8_t track_bcd = buf.qbuf[1];
uint8_t index_bcd = buf.qbuf[2];
uint8_t rm_bcd = buf.qbuf[3];
uint8_t rs_bcd = buf.qbuf[4];
uint8_t rf_bcd = buf.qbuf[5];
uint8_t am_bcd = buf.qbuf[7];
uint8_t as_bcd = buf.qbuf[8];
uint8_t 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)
{
log_cb(RETRO_LOG_ERROR, "Garbage subchannel Q data detected(bad BCD/out of range): %02x:%02x:%02x %02x:%02x:%02x\n", rm_bcd, rs_bcd, rf_bcd, am_bcd, as_bcd, af_bcd);
return false;
}
else
{
int lba = ((BCD_to_U8(am_bcd) * 60 + BCD_to_U8(as_bcd)) * 75 + BCD_to_U8(af_bcd)) - 150;
uint8_t track = BCD_to_U8(track_bcd);
if(prev_lba != INT_MAX && abs(lba - prev_lba) > 100)
{
log_cb(RETRO_LOG_ERROR, "Garbage subchannel Q data detected(excessively large jump in AMSF)\n");
return false;
}
if(abs((int)(lba - s)) > 100)
{
log_cb(RETRO_LOG_ERROR, "Garbage subchannel Q data detected(AMSF value is out of tolerance)\n");
return false;
}
prev_lba = lba;
if(track < prev_track)
{
log_cb(RETRO_LOG_ERROR, "Garbage subchannel Q data detected(bad track number)\n");
return false;
}
prev_track = track;
}
checksum_pass_counter++;
}
lba = ((BCD_to_U8(am_bcd) * 60 + BCD_to_U8(as_bcd)) * 75 + BCD_to_U8(af_bcd)) - 150;
track = BCD_to_U8(track_bcd);
prev_lba = lba;
if(track < prev_track)
{
MDFN_Error(0, _("Garbage subchannel Q data detected(bad track number)"));
return false;
}
prev_track = track;
checksum_pass_counter++;
}
}
//printf("%u/%u\n", checksum_pass_counter, img_numsectors);
return true;
}
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();
if (img_stream)
delete[] img_stream;
if (sub_data)
delete[] sub_data;
}
bool CDAccess_CCD::Read_Raw_Sector(uint8 *buf, int32 lba)
void CDAccess_CCD::Read_Raw_Sector(uint8_t *buf, int32_t lba)
{
uint8_t sub_buf[96];
if(lba < 0 || (size_t)lba >= img_numsectors)
if(lba < 0)
{
MDFN_Error(0, _("LBA out of range."));
return false;
synth_udapp_sector_lba(0xFF, tocd, lba, 0, buf);
return;
}
if((size_t)lba >= img_numsectors)
{
synth_leadout_sector_lba(0xFF, tocd, lba, buf);
return;
}
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_data[lba * 96], buf + 2352);
}
subpw_interleave(sub_buf, buf + 2352);
bool CDAccess_CCD::Fast_Read_Raw_PW_TSRE(uint8_t* pwbuf, int32_t lba) const noexcept
{
if(lba < 0)
{
subpw_synth_udapp_lba(tocd, lba, 0, pwbuf);
return true;
}
if((size_t)lba >= img_numsectors)
{
subpw_synth_leadout_lba(tocd, lba, pwbuf);
return true;
}
subpw_interleave(&sub_data[lba * 96], pwbuf);
return true;
}
bool CDAccess_CCD::Read_TOC(TOC *toc)
void CDAccess_CCD::Read_TOC(TOC *toc)
{
*toc = tocd;
return true;
}
void CDAccess_CCD::Eject(bool eject_status)
{
}

View File

@ -1,53 +1,52 @@
/* 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
*/
/******************************************************************************/
/* Mednafen - Multi-system Emulator */
/******************************************************************************/
/* CDAccess_CCD.h:
** Copyright (C) 2013-2016 Mednafen Team
**
** 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.,
** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef _CDROM_CDACCESS_CCD_H_
#define _CDROM_CDACCESS_CCD_H_
#include <mednafen/FileStream.h>
#include <mednafen/MemoryStream.h>
#include "../FileStream.h"
#include "../MemoryStream.h"
#include "CDAccess.h"
#include <vector>
class CDAccess_CCD : public CDAccess
{
public:
CDAccess_CCD(bool *success, const char *path, bool image_memcache);
CDAccess_CCD(const std::string& path, bool image_memcache);
virtual ~CDAccess_CCD();
virtual bool Read_Raw_Sector(uint8_t *buf, int32_t lba);
virtual void Read_Raw_Sector(uint8 *buf, int32 lba);
virtual bool Read_TOC(TOC *toc);
virtual bool Fast_Read_Raw_PW_TSRE(uint8* pwbuf, int32 lba) const noexcept;
virtual void Eject(bool eject_status);
virtual void Read_TOC(TOC *toc);
private:
bool Load(const char *path, bool image_memcache);
bool Load(const std::string& path, bool image_memcache);
void Cleanup(void);
bool CheckSubQSanity(void);
Stream* img_stream;
Stream* sub_stream;
Stream *img_stream;
uint8_t *sub_data;
size_t img_numsectors;
TOC tocd;
};
#endif

File diff suppressed because it is too large Load Diff

View File

@ -2,9 +2,12 @@
#define __MDFN_CDACCESS_IMAGE_H
#include <map>
#include <array>
#include "CDUtility.h"
class Stream;
class AudioReader;
class CDAFReader;
struct CDRFILE_TRACK_INFO
{
@ -18,7 +21,7 @@ struct CDRFILE_TRACK_INFO
int32_t postgap;
int32_t index[2];
int32_t index[100];
int32_t sectors; // Not including pregap sectors!
Stream *fp;
@ -29,21 +32,22 @@ struct CDRFILE_TRACK_INFO
uint32_t LastSamplePos;
AudioReader *AReader;
CDAFReader *AReader;
};
class CDAccess_Image : public CDAccess
{
public:
CDAccess_Image(bool *success, const char *path, bool image_memcache);
CDAccess_Image(const std::string& path, bool image_memcache);
virtual ~CDAccess_Image();
virtual bool Read_Raw_Sector(uint8_t *buf, int32_t lba);
virtual void Read_Raw_Sector(uint8_t *buf, int32_t lba);
virtual bool Read_TOC(TOC *toc);
virtual bool Fast_Read_Raw_PW_TSRE(uint8_t* pwbuf, int32_t lba) const noexcept;
virtual void Read_TOC(TOC *toc);
virtual void Eject(bool eject_status);
private:
int32_t NumTracks;
@ -52,26 +56,21 @@ class CDAccess_Image : public CDAccess
int32_t total_sectors;
uint8_t disc_type;
CDRFILE_TRACK_INFO Tracks[100]; // Track #0(HMM?) through 99
TOC toc;
struct cpp11_array_doodad
{
uint8 data[12];
};
std::map<uint32, cpp11_array_doodad> SubQReplaceMap;
std::map<uint32_t, std::array<uint8_t, 12>> SubQReplaceMap;
std::string base_dir;
bool ImageOpen(const char *path, bool image_memcache);
int LoadSBI(const char* sbi_path);
bool ImageOpen(const std::string& path, bool image_memcache);
bool LoadSBI(const std::string& sbi_path);
void GenerateTOC(void);
void Cleanup(void);
// MakeSubPQ will OR the simulated P and Q subchannel data into SubPWBuf.
void MakeSubPQ(int32_t lba, uint8_t *SubPWBuf);
int32_t MakeSubPQ(int32_t lba, uint8_t *SubPWBuf) const;
bool 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);
bool 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_t GetSectorCount(CDRFILE_TRACK_INFO *track);
};

View File

@ -17,16 +17,14 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "../mednafen.h"
#include "CDUtility.h"
#include "edc_crc32.h"
#include "galois.h"
#include "l-ec.h"
#include "recover-raw.h"
#include "dvdisaster.h"
#include "lec.h"
#include <assert.h>
/* lookup table for crc calculation */
// lookup table for crc calculation
static uint16_t subq_crctab[256] =
{
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108,
@ -61,17 +59,18 @@ static uint16_t subq_crctab[256] =
};
static uint8_t cdutil_scramble_table[2352 - 12];
static uint8_t scramble_table[2352 - 12];
static bool CDUtility_Inited = false;
static void InitScrambleTable(void)
{
unsigned i, b;
unsigned i;
unsigned cv = 1;
for(i = 12; i < 2352; i++)
{
unsigned b;
unsigned char z = 0;
for(b = 0; b < 8; b++)
@ -82,21 +81,23 @@ static void InitScrambleTable(void)
cv = (cv >> 1) | (feedback << 14);
}
cdutil_scramble_table[i - 12] = z;
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();
if(CDUtility_Inited)
return;
InitScrambleTable();
lec_tables_init();
Init_LEC_Correct();
CDUtility_Inited = true;
}
InitScrambleTable();
CDUtility_Inited = true;
}
void encode_mode0_sector(uint32_t aba, uint8_t *sector_data)
@ -152,9 +153,10 @@ bool edc_lec_check_and_correct(uint8_t *sector_data, bool xa)
bool subq_check_checksum(const uint8_t *SubQBuf)
{
unsigned i;
uint16_t crc = 0;
uint16_t stored_crc = SubQBuf[0xA] << 8;
uint16_t crc = 0;
uint16_t stored_crc = 0;
stored_crc = SubQBuf[0xA] << 8;
stored_crc |= SubQBuf[0xB];
for(i = 0; i < 0xA; i++)
@ -173,7 +175,7 @@ void subq_generate_checksum(uint8_t *buf)
for(i = 0; i < 0xA; i++)
crc = subq_crctab[(crc >> 8) ^ buf[i]] ^ (crc << 8);
/* Checksum */
// Checksum
buf[0xa] = ~(crc >> 8);
buf[0xb] = ~(crc);
}
@ -181,7 +183,6 @@ void subq_generate_checksum(uint8_t *buf)
void subq_deinterleave(const uint8_t *SubPWBuf, uint8_t *qbuf)
{
unsigned i;
memset(qbuf, 0, 0xC);
for(i = 0; i < 96; i++)
@ -192,30 +193,31 @@ void subq_deinterleave(const uint8_t *SubPWBuf, uint8_t *qbuf)
// Deinterleaves 96 bytes of subchannel P-W data from 96 bytes of interleaved subchannel PW data.
void subpw_deinterleave(const uint8_t *in_buf, uint8_t *out_buf)
{
unsigned ch, i;
unsigned ch;
assert(in_buf != out_buf);
memset(out_buf, 0, 96);
for(ch = 0; ch < 8; ch++)
{
unsigned i;
for(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_t *in_buf, uint8_t *out_buf)
{
unsigned d, bitpoodle, ch;
unsigned d;
assert(in_buf != out_buf);
for(d = 0; d < 12; d++)
{
unsigned bitpoodle;
for(bitpoodle = 0; bitpoodle < 8; bitpoodle++)
{
unsigned ch;
uint8_t rawb = 0;
for(ch = 0; ch < 8; ch++)
@ -231,24 +233,29 @@ void subpw_interleave(const uint8_t *in_buf, uint8_t *out_buf)
// 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 struct TOC *toc, const int32_t lba, uint8_t* SubPWBuf)
void subpw_synth_leadout_lba(const TOC& toc, const int32_t lba, uint8_t* SubPWBuf)
{
unsigned i;
uint8_t buf[0xC];
uint32_t lba_relative = lba - toc->tracks[100].lba;
uint32_t f = (lba_relative % 75);
uint32_t s = ((lba_relative / 75) % 60);
uint32_t m = (lba_relative / 75 / 60);
uint32_t fa = (lba + 150) % 75;
uint32_t sa = ((lba + 150) / 75) % 60;
uint32_t ma = ((lba + 150) / 75 / 60);
uint32_t lba_relative;
uint32_t ma, sa, fa;
uint32_t m, s, f;
uint8_t adr = 0x1; // Q channel data encodes position
uint8_t control = toc->tracks[100].control;
lba_relative = lba - toc.tracks[100].lba;
if (toc->tracks[toc->last_track].valid)
control |= toc->tracks[toc->last_track].control & 0x4;
else if (toc->disc_type == DISC_TYPE_CD_I)
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_t adr = 0x1; // Q channel data encodes position
uint8_t control = toc.tracks[100].control;
if(toc.tracks[toc.last_track].valid)
control |= toc.tracks[toc.last_track].control & 0x4;
else if(toc.disc_type == DISC_TYPE_CD_I)
control |= 0x4;
memset(buf, 0, 0xC);
@ -256,25 +263,25 @@ void subpw_synth_leadout_lba(const struct TOC *toc, const int32_t lba, uint8_t*
buf[1] = 0xAA;
buf[2] = 0x01;
/* Track relative MSF address */
// 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 */
buf[6] = 0; // Zerroooo
/* Absolute MSF address */
// 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(i = 0; i < 96; i++)
for(int i = 0; i < 96; i++)
SubPWBuf[i] = (((buf[i >> 3] >> (7 - (i & 0x7))) & 1) ? 0x40 : 0x00) | 0x80;
}
void synth_leadout_sector_lba(uint8_t mode, const struct TOC *toc, const int32_t lba, uint8_t* out_buf)
void synth_leadout_sector_lba(uint8_t mode, const TOC& toc, const int32_t lba, uint8_t* out_buf)
{
memset(out_buf, 0, 2352 + 96);
subpw_synth_leadout_lba(toc, lba, out_buf + 2352);
@ -283,7 +290,7 @@ void synth_leadout_sector_lba(uint8_t mode, const struct TOC *toc, const int32_t
{
if(mode == 0xFF)
{
if(toc->disc_type == DISC_TYPE_CD_XA || toc->disc_type == DISC_TYPE_CD_I)
if(toc.disc_type == DISC_TYPE_CD_XA || toc.disc_type == DISC_TYPE_CD_I)
mode = 0x02;
else
mode = 0x01;
@ -308,17 +315,115 @@ void synth_leadout_sector_lba(uint8_t mode, const struct TOC *toc, const int32_t
}
}
// ISO/IEC 10149:1995 (E): 20.2
//
void subpw_synth_udapp_lba(const TOC& toc, const int32_t lba, const int32_t lba_subq_relative_offs, uint8_t* SubPWBuf)
{
uint8_t buf[0xC];
uint32_t lba_relative;
uint32_t ma, sa, fa;
uint32_t m, s, f;
if(lba < -150 || lba >= 0)
printf("[BUG] subpw_synth_udapp_lba() lba out of range --- %d\n", lba);
{
int32_t lba_tmp = lba + lba_subq_relative_offs;
if(lba_tmp < 0)
lba_relative = 0 - 1 - lba_tmp;
else
lba_relative = lba_tmp - 0;
}
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_t adr = 0x1; // Q channel data encodes position
uint8_t control;
if(toc.disc_type == DISC_TYPE_CD_I && toc.first_track > 1)
control = 0x4;
else if(toc.tracks[toc.first_track].valid)
control = toc.tracks[toc.first_track].control;
else
control = 0x0;
memset(buf, 0, 0xC);
buf[0] = (adr << 0) | (control << 4);
buf[1] = U8_to_BCD(toc.first_track);
buf[2] = U8_to_BCD(0x00);
// 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_udapp_sector_lba(uint8_t mode, const TOC& toc, const int32_t lba, int32_t lba_subq_relative_offs, uint8_t* out_buf)
{
memset(out_buf, 0, 2352 + 96);
subpw_synth_udapp_lba(toc, lba, lba_subq_relative_offs, out_buf + 2352);
if(out_buf[2352 + 1] & 0x40)
{
if(mode == 0xFF)
{
if(toc.disc_type == DISC_TYPE_CD_XA || toc.disc_type == DISC_TYPE_CD_I)
mode = 0x02;
else
mode = 0x01;
}
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[12 + 6] = 0x20;
out_buf[12 + 10] = 0x20;
encode_mode2_form2_sector(LBA_to_ABA(lba), out_buf);
break;
}
}
}
/* ISO/IEC 10149:1995 (E): 20.2 */
#if 0
/* TODO/FIXME - missing functions */
void subpw_synth_udapp_lba(const TOC& toc, const int32 lba, const int32 lba_subq_relative_offs, uint8* SubPWBuf);
void synth_udapp_sector_lba(uint8 mode, const TOC& toc, const int32 lba, int32 lba_subq_relative_offs, uint8* out_buf);
bool subq_extrapolate(const uint8_t *subq_input, int32_t position_delta, uint8_t *subq_output)
{
assert(subq_check_checksum(subq_input));
subq_generate_checksum(subq_output);
}
#endif
void scrambleize_data_sector(uint8_t *sector_data)
{
unsigned i;
for(i = 12; i < 2352; i++)
sector_data[i] ^= cdutil_scramble_table[i - 12];
sector_data[i] ^= scramble_table[i - 12];
}

View File

@ -1,233 +1,233 @@
#ifndef __MDFN_CDROM_CDUTILITY_H
#define __MDFN_CDROM_CDUTILITY_H
#include <stdint.h>
#include <string.h>
// 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);
#include <boolean.h>
#include <retro_inline.h>
// 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
#ifdef __cplusplus
extern "C" {
#endif
enum
{
ADR_NOQINFO = 0x00,
ADR_CURPOS = 0x01,
ADR_MCN = 0x02,
ADR_ISRC = 0x03
};
enum
{
ADR_NOQINFO = 0x00,
ADR_CURPOS = 0x01,
ADR_MCN = 0x02,
ADR_ISRC = 0x03
};
struct TOC_Track
{
uint8_t adr;
uint8_t control;
uint32_t lba;
bool valid; /* valid/present; oh CD-i... */
};
// 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.
};
struct TOC_Track
{
uint8_t adr;
uint8_t control;
uint32_t lba;
bool valid; // valid/present; oh CD-i...
};
enum
{
DISC_TYPE_CDDA_OR_M1 = 0x00,
DISC_TYPE_CD_I = 0x10,
DISC_TYPE_CD_XA = 0x20
};
// 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. */
};
struct TOC
{
uint8_t first_track;
uint8_t last_track;
uint8_t disc_type;
struct TOC_Track tracks[100 + 1];
};
enum
{
DISC_TYPE_CDDA_OR_M1 = 0x00,
DISC_TYPE_CD_I = 0x10,
DISC_TYPE_CD_XA = 0x20
};
// 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);
struct TOC
{
INLINE TOC()
{
Clear();
}
// 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
INLINE void Clear(void)
{
first_track = last_track = 0;
disc_type = 0;
static INLINE void TOC_Clear(struct TOC *toc)
{
if (!toc)
return;
memset(tracks, 0, sizeof(tracks)); // FIXME if we change TOC_Track to non-POD type.
}
toc->first_track = 0;
toc->last_track = 0;
toc->disc_type = 0;
memset(toc->tracks, 0, sizeof(toc->tracks));
}
static INLINE int TOC_FindTrackByLBA(struct TOC *toc, uint32_t LBA)
{
INLINE int FindTrackByLBA(uint32_t LBA) const
{
int32_t track;
int32_t lvt = 0;
for(track = toc->first_track; track <= (toc->last_track + 1); track++)
for(track = 1; track <= 100; track++)
{
if(track == (toc->last_track + 1))
{
if(LBA < toc->tracks[100].lba)
return(track - 1);
}
else
{
if(LBA < toc->tracks[track].lba)
return(track - 1);
}
if(!tracks[track].valid)
continue;
if(LBA < tracks[track].lba)
break;
lvt = track;
}
return 0;
}
return(lvt);
}
// Address conversion functions.
static INLINE uint32_t AMSF_to_ABA(int32_t m_a, int32_t s_a, int32_t f_a)
{
return(f_a + 75 * s_a + 75 * 60 * m_a);
}
uint8_t first_track;
uint8_t last_track;
uint8_t disc_type;
TOC_Track tracks[100 + 1]; // [0] is unused, [100] is for the leadout track.
};
static INLINE void ABA_to_AMSF(uint32_t aba, uint8_t *m_a, uint8_t *s_a, uint8_t *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);
}
//
// Address conversion functions.
//
static INLINE uint32_t AMSF_to_ABA(int32_t m_a, int32_t s_a, int32_t f_a)
{
return(f_a + 75 * s_a + 75 * 60 * m_a);
}
static INLINE int32_t ABA_to_LBA(uint32_t aba)
{
return(aba - 150);
}
static INLINE void ABA_to_AMSF(uint32_t aba, uint8_t *m_a, uint8_t *s_a, uint8_t *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 uint32_t LBA_to_ABA(int32_t lba)
{
return(lba + 150);
}
static INLINE int32_t ABA_to_LBA(uint32_t aba)
{
return(aba - 150);
}
static INLINE int32_t AMSF_to_LBA(uint8_t m_a, uint8_t s_a, uint8_t f_a)
{
return(ABA_to_LBA(AMSF_to_ABA(m_a, s_a, f_a)));
}
static INLINE uint32_t LBA_to_ABA(int32_t lba)
{
return(lba + 150);
}
static INLINE void LBA_to_AMSF(int32_t lba, uint8_t *m_a, uint8_t *s_a, uint8_t *f_a)
{
ABA_to_AMSF(LBA_to_ABA(lba), m_a, s_a, f_a);
}
static INLINE int32_t AMSF_to_LBA(uint8_t m_a, uint8_t s_a, uint8_t f_a)
{
return(ABA_to_LBA(AMSF_to_ABA(m_a, s_a, f_a)));
}
//
// BCD conversion functions
//
static INLINE bool BCD_is_valid(uint8_t bcd_number)
{
if((bcd_number & 0xF0) >= 0xA0)
return(false);
static INLINE void LBA_to_AMSF(int32_t lba, uint8_t *m_a, uint8_t *s_a, uint8_t *f_a)
{
ABA_to_AMSF(LBA_to_ABA(lba), m_a, s_a, f_a);
}
if((bcd_number & 0x0F) >= 0x0A)
return(false);
/* BCD conversion functions */
static INLINE bool BCD_is_valid(uint8_t bcd_number)
{
if((bcd_number & 0xF0) >= 0xA0)
return(false);
return(true);
}
if((bcd_number & 0x0F) >= 0x0A)
return(false);
static INLINE uint8_t BCD_to_U8(uint8_t bcd_number)
{
return( ((bcd_number >> 4) * 10) + (bcd_number & 0x0F) );
}
return(true);
}
static INLINE uint8_t U8_to_BCD(uint8_t num)
{
return( ((num / 10) << 4) + (num % 10) );
}
static INLINE uint8_t BCD_to_U8(uint8_t bcd_number)
{
return( ((bcd_number >> 4) * 10) + (bcd_number & 0x0F) );
}
// should always perform the conversion, even if the bcd number is invalid.
static INLINE bool BCD_to_U8_check(uint8_t bcd_number, uint8_t *out_number)
{
*out_number = BCD_to_U8(bcd_number);
static INLINE uint8_t U8_to_BCD(uint8_t num)
{
return( ((num / 10) << 4) + (num % 10) );
}
if(!BCD_is_valid(bcd_number))
return(false);
// should always perform the conversion, even if the bcd number is invalid.
static INLINE bool BCD_to_U8_check(uint8_t bcd_number, uint8_t *out_number)
{
*out_number = BCD_to_U8(bcd_number);
return(true);
}
if(!BCD_is_valid(bcd_number))
return(false);
//
// 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_t aba, uint8_t *sector_data);
void encode_mode1_sector(uint32_t aba, uint8_t *sector_data); // 2048 bytes of user data at offset 16
void encode_mode2_sector(uint32_t aba, uint8_t *sector_data); // 2336 bytes of user data at offset 16
void encode_mode2_form1_sector(uint32_t aba, uint8_t *sector_data); // 2048+8 bytes of user data at offset 16
void encode_mode2_form2_sector(uint32_t aba, uint8_t *sector_data); // 2324+8 bytes of user data at offset 16
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_t aba, uint8_t *sector_data);
void encode_mode1_sector(uint32_t aba, uint8_t *sector_data); // 2048 bytes of user data at offset 16
void encode_mode2_sector(uint32_t aba, uint8_t *sector_data); // 2336 bytes of user data at offset 16
void encode_mode2_form1_sector(uint32_t aba, uint8_t *sector_data); // 2048+8 bytes of user data at offset 16
void encode_mode2_form2_sector(uint32_t aba, uint8_t *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(uint8_t mode, const struct TOC *toc, const int32_t lba, uint8_t* out_buf);
// User data area pre-pause(MSF 00:00:00 through 00:01:74), lba -150 through -1
// out_buf must be able to contain 2352+96 bytes.
// "mode" is not used if the area is to be encoded as audio.
// pass 0xFF for "mode" for "don't know", and to make guess based on the TOC.
void synth_udapp_sector_lba(uint8_t mode, const TOC& toc, const int32_t lba, int32_t lba_subq_relative_offs, uint8_t* out_buf);
void subpw_synth_udapp_lba(const TOC& toc, const int32_t lba, const int32_t lba_subq_relative_offs, uint8_t* SubPWBuf);
//
// User data error detection and correction
//
// out_buf must be able to contain 2352+96 bytes.
// "mode" is not used if the area is to be encoded as audio.
// pass 0xFF for "mode" for "don't know", and to make guess based on the TOC.
void synth_leadout_sector_lba(uint8_t mode, const TOC& toc, const int32_t lba, uint8_t* out_buf);
void subpw_synth_leadout_lba(const TOC& toc, const int32_t lba, uint8_t* SubPWBuf);
// 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_t *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_t *sector_data, bool xa);
//
// User data error detection and correction
//
//
// Subchannel(Q in particular) functions
//
// 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_t *sector_data, bool xa);
// Returns false on checksum mismatch, true on match.
bool subq_check_checksum(const uint8_t *subq_buf);
// 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.
//
// Note: mode 2 form 1 L-EC data can't correct errors in the 4-byte sector header(address + mode),
// but the error(s) will still be detected by EDC.
bool edc_lec_check_and_correct(uint8_t *sector_data, bool xa);
// 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_t *subq_buf);
//
// Subchannel(Q in particular) functions
//
// Deinterleaves 12 bytes of subchannel Q data from 96 bytes of interleaved subchannel PW data.
void subq_deinterleave(const uint8_t *subpw_buf, uint8_t *subq_buf);
// Returns false on checksum mismatch, true on match.
bool subq_check_checksum(const uint8_t *subq_buf);
// Deinterleaves 96 bytes of subchannel P-W data from 96 bytes of interleaved subchannel PW data.
void subpw_deinterleave(const uint8_t *in_buf, uint8_t *out_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_t *subq_buf);
// Interleaves 96 bytes of subchannel P-W data from 96 bytes of uninterleaved subchannel PW data.
void subpw_interleave(const uint8_t *in_buf, uint8_t *out_buf);
// Deinterleaves 12 bytes of subchannel Q data from 96 bytes of interleaved subchannel PW data.
void subq_deinterleave(const uint8_t *subpw_buf, uint8_t *subq_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_t *subq_input, int32_t position_delta, uint8_t *subq_output);
// Deinterleaves 96 bytes of subchannel P-W data from 96 bytes of interleaved subchannel PW data.
void subpw_deinterleave(const uint8_t *in_buf, uint8_t *out_buf);
// (De)Scrambles data sector.
void scrambleize_data_sector(uint8_t *sector_data);
// Interleaves 96 bytes of subchannel P-W data from 96 bytes of uninterleaved subchannel PW data.
void subpw_interleave(const uint8_t *in_buf, uint8_t *out_buf);
#ifdef __cplusplus
}
#endif
// 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_t *subq_input, int32_t position_delta, uint8_t *subq_output);
// (De)Scrambles data sector.
void scrambleize_data_sector(uint8_t *sector_data);
#endif

View File

@ -1,6 +1,7 @@
#ifndef __MDFN_SIMPLEFIFO_H
#define __MDFN_SIMPLEFIFO_H
#include <vector>
#include <assert.h>
#include "../math_ops.h"
@ -11,25 +12,54 @@ class SimpleFIFO
public:
// Constructor
SimpleFIFO(uint32 the_size)
SimpleFIFO(uint32 the_size) // Size should be a power of 2!
{
/* Size should be a power of 2! */
assert(the_size && !(the_size & (the_size - 1)));
data = (T*)malloc(the_size * sizeof(T));
size = the_size;
read_pos = 0;
write_pos = 0;
in_count = 0;
data.resize(round_up_pow2(the_size));
size = the_size;
read_pos = 0;
write_pos = 0;
in_count = 0;
}
// Destructor
INLINE ~SimpleFIFO()
{
if (data)
free(data);
}
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);
@ -50,7 +80,7 @@ class SimpleFIFO
if(!peek)
{
read_pos = (read_pos + 1) & (size - 1);
read_pos = (read_pos + 1) & (data.size() - 1);
in_count--;
}
@ -72,7 +102,7 @@ class SimpleFIFO
{
data[write_pos] = *happy_data;
write_pos = (write_pos + 1) & (size - 1);
write_pos = (write_pos + 1) & (data.size() - 1);
in_count++;
happy_data++;
happy_count--;
@ -98,14 +128,8 @@ class SimpleFIFO
in_count = 0;
}
INLINE void SaveStatePostLoad(void)
{
read_pos %= size;
write_pos %= size;
in_count %= (size + 1);
}
T* data;
//private:
std::vector<T> data;
uint32 size;
uint32 read_pos; // Read position
uint32 write_pos; // Write position

View File

@ -1,171 +0,0 @@
/* 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 "../tremor/ivorbisfile.h"
#include <string.h>
#include <errno.h>
#include <time.h>
#include "../general.h"
#include "../mednafen-endian.h"
AudioReader::AudioReader() : LastReadPos(0)
{
}
AudioReader::~AudioReader()
{
}
int64_t AudioReader::Read_(int16_t *buffer, int64_t frames)
{
abort();
return(false);
}
bool AudioReader::Seek_(int64_t frame_offset)
{
abort();
return(false);
}
int64_t AudioReader::FrameCount(void)
{
abort();
return(0);
}
class OggVorbisReader : public AudioReader
{
public:
OggVorbisReader(Stream *fp);
~OggVorbisReader();
int64_t Read_(int16_t *buffer, int64_t frames);
bool Seek_(int64_t frame_offset);
int64_t FrameCount(void);
private:
OggVorbis_File ovfile;
};
static size_t iov_read_func(void *ptr, size_t size, size_t nmemb, void *user_data)
{
Stream *fw = (Stream*)user_data;
if(!size || !fw)
return(0);
return fw->read(ptr, size * nmemb, false) / size;
}
static int iov_seek_func(void *user_data, ogg_int64_t offset, int whence)
{
Stream *fw = (Stream*)user_data;
if (fw)
fw->seek(offset, whence);
return(0);
}
static int iov_close_func(void *user_data)
{
Stream *fw = (Stream*)user_data;
if (fw)
fw->close();
return(0);
}
static long iov_tell_func(void *user_data)
{
Stream *fw = (Stream*)user_data;
if (!fw)
return -1;
return fw->tell();
}
OggVorbisReader::OggVorbisReader(Stream *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_t OggVorbisReader::Read_(int16_t *buffer, int64_t frames)
{
uint8 *tw_buf = (uint8 *)buffer;
int cursection = 0;
long toread = frames * sizeof(int16_t) * 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_t) / 2);
}
bool OggVorbisReader::Seek_(int64_t frame_offset)
{
ov_pcm_seek(&ovfile, frame_offset);
return(true);
}
int64_t OggVorbisReader::FrameCount(void)
{
return(ov_pcm_total(&ovfile, -1));
}
AudioReader *AR_Open(Stream *fp)
{
return new OggVorbisReader(fp);
}

View File

@ -1,41 +0,0 @@
#ifndef __MDFN_AUDIOREADER_H
#define __MDFN_AUDIOREADER_H
#include "../Stream.h"
class AudioReader
{
public:
AudioReader();
virtual ~AudioReader();
virtual int64_t FrameCount(void);
INLINE int64_t Read(int64_t frame_offset, int16_t *buffer, int64_t frames)
{
int64_t ret;
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_t Read_(int16_t *buffer, int64_t frames);
virtual bool Seek_(int64_t frame_offset);
int64_t LastReadPos;
};
// AR_Open(), and AudioReader, will NOT take "ownership" of the Stream object(IE it won't ever delete it). Though it does assume it has exclusive access
// to it for as long as the AudioReader object exists.
AudioReader *AR_Open(Stream *fp);
#endif

View File

@ -25,24 +25,21 @@
#include <algorithm>
#include <boolean.h>
#include <rthreads/rthreads.h>
#include <retro_miscellaneous.h>
#include "../../libretro.h"
extern retro_log_printf_t log_cb;
enum
{
// Status/Error messages
CDIF_MSG_DONE = 0,
CDIF_MSG_INFO,
CDIF_MSG_FATAL_ERROR,
/* 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,
CDIF_MSG_READ_SECTOR,
CDIF_MSG_EJECT
/* Command messages. */
CDIF_MSG_DIEDIEDIE, /* Emu -> read */
CDIF_MSG_READ_SECTOR /* Emu -> read
args[0] = lba
*/
};
class CDIF_Message
@ -50,92 +47,25 @@ 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_, uint32_t arg0 = 0, uint32_t arg1 = 0, uint32_t arg2 = 0, uint32_t arg3 = 0);
CDIF_Message(unsigned int message_, const std::string &str);
~CDIF_Message();
unsigned int message;
uint32 args[4];
uint32_t args[4];
void *parg;
std::string str_message;
};
class CDIF_Queue
{
public:
CDIF_Queue();
~CDIF_Queue();
bool Read(CDIF_Message *message, bool blocking = true);
void Write(const CDIF_Message &message);
private:
std::queue<CDIF_Message> ze_queue;
slock_t *ze_mutex;
scond_t *ze_cond;
};
typedef struct
{
bool valid;
bool error;
uint32 lba;
uint8 data[2352 + 96];
int32_t lba;
uint8_t data[2352 + 96];
} CDIF_Sector_Buffer;
/* TODO: prohibit copy constructor */
class CDIF_MT : public CDIF
{
public:
CDIF_MT(CDAccess *cda);
virtual ~CDIF_MT();
virtual void HintReadSector(uint32 lba);
virtual bool ReadRawSector(uint8 *buf, uint32 lba);
virtual bool ReadRawSectorPWOnly(uint8 *buf, uint32 lba, bool hint_fullread);
// 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);
// FIXME: Semi-private:
int ReadThreadStart(void);
private:
CDAccess *disc_cdaccess;
sthread_t *CDReadThread;
// Queue for messages to the read thread.
CDIF_Queue ReadThreadQueue;
// Queue for messages to the emu thread.
CDIF_Queue EmuThreadQueue;
enum { SBSize = 256 };
CDIF_Sector_Buffer SectorBuffers[SBSize];
uint32 SBWritePos;
slock_t *SBMutex;
scond_t *SBCond;
//
// Read-thread-only:
//
bool RT_EjectDisc(bool eject_status, bool skip_actual_eject = false);
uint32 ra_lba;
int ra_count;
uint32 last_read_lba;
};
/* TODO: prohibit copy constructor */
// TODO: prohibit copy constructor
class CDIF_ST : public CDIF
{
public:
@ -143,18 +73,17 @@ class CDIF_ST : public CDIF
CDIF_ST(CDAccess *cda);
virtual ~CDIF_ST();
virtual void HintReadSector(uint32 lba);
virtual bool ReadRawSector(uint8 *buf, uint32 lba);
virtual bool ReadRawSectorPWOnly(uint8 *buf, uint32 lba, bool hint_fullread);
virtual bool Eject(bool eject_status);
virtual void HintReadSector(int32_t lba);
virtual bool ReadRawSector(uint8_t *buf, int32_t lba);
virtual bool ReadRawSectorPWOnly(uint8_t* pwbuf, int32_t lba, bool hint_fullread);
private:
CDAccess *disc_cdaccess;
};
CDIF::CDIF() : UnrecoverableError(false), DiscEjected(false)
CDIF::CDIF() : UnrecoverableError(false)
{
TOC_Clear(&disc_toc);
}
CDIF::~CDIF()
@ -170,7 +99,7 @@ CDIF_Message::CDIF_Message()
memset(args, 0, sizeof(args));
}
CDIF_Message::CDIF_Message(unsigned int message_, uint32 arg0, uint32 arg1, uint32 arg2, uint32 arg3)
CDIF_Message::CDIF_Message(unsigned int message_, uint32_t arg0, uint32_t arg1, uint32_t arg2, uint32_t arg3)
{
message = message_;
args[0] = arg0;
@ -190,242 +119,7 @@ CDIF_Message::~CDIF_Message()
}
CDIF_Queue::CDIF_Queue()
{
ze_mutex = slock_new();
ze_cond = scond_new();
}
CDIF_Queue::~CDIF_Queue()
{
slock_free(ze_mutex);
scond_free(ze_cond);
}
// Returns false if message not read, true if it was read. Will always return true if "blocking" is set.
// Will throw MDFN_Error if the read message code is CDIF_MSG_FATAL_ERROR
bool CDIF_Queue::Read(CDIF_Message *message, bool blocking)
{
bool ret = true;
slock_lock((slock_t*)ze_mutex);
if(blocking)
{
while(ze_queue.size() == 0) // while, not just if.
scond_wait((scond_t*)ze_cond, (slock_t*)ze_mutex);
}
if(ze_queue.size() == 0)
ret = false;
else
{
*message = ze_queue.front();
ze_queue.pop();
}
slock_unlock((slock_t*)ze_mutex);
if(ret && message->message == CDIF_MSG_FATAL_ERROR)
{
MDFN_Error(0, "%s", message->str_message.c_str());
return false;
}
return(ret);
}
void CDIF_Queue::Write(const CDIF_Message &message)
{
slock_lock((slock_t*)ze_mutex);
ze_queue.push(message);
scond_signal((scond_t*)ze_cond); // Signal while the mutex is held to prevent icky race conditions
slock_unlock((slock_t*)ze_mutex);
}
bool CDIF_MT::RT_EjectDisc(bool eject_status, bool skip_actual_eject)
{
int32_t old_de = DiscEjected;
DiscEjected = eject_status;
if(old_de != DiscEjected)
{
if(!skip_actual_eject)
disc_cdaccess->Eject(eject_status);
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)
{
MDFN_Error(0, _("TOC first(%d)/last(%d) track numbers bad."), disc_toc.first_track, disc_toc.last_track);
return false;
}
}
SBWritePos = 0;
ra_lba = 0;
ra_count = 0;
last_read_lba = ~0U;
memset(SectorBuffers, 0, SBSize * sizeof(CDIF_Sector_Buffer));
}
return true;
}
struct RTS_Args
{
CDIF_MT *cdif_ptr;
};
static int ReadThreadStart_C(void *v_arg)
{
RTS_Args *args = (RTS_Args *)v_arg;
return args->cdif_ptr->ReadThreadStart();
}
int CDIF_MT::ReadThreadStart()
{
bool Running = true;
DiscEjected = true;
SBWritePos = 0;
ra_lba = 0;
ra_count = 0;
last_read_lba = ~0U;
RT_EjectDisc(false, true);
EmuThreadQueue.Write(CDIF_Message(CDIF_MSG_DONE));
while(Running)
{
CDIF_Message msg;
// Only do a blocking-wait for a message if we don't have any sectors to read-ahead.
// MDFN_DispMessage("%d %d %d\n", last_read_lba, ra_lba, ra_count);
if(ReadThreadQueue.Read(&msg, ra_count ? false : true))
{
switch(msg.message)
{
case CDIF_MSG_DIEDIEDIE:
Running = false;
break;
case CDIF_MSG_EJECT:
RT_EjectDisc(msg.args[0]);
EmuThreadQueue.Write(CDIF_Message(CDIF_MSG_DONE));
break;
case CDIF_MSG_READ_SECTOR:
{
static const int max_ra = 16;
static const int initial_ra = 1;
static const int speedmult_ra = 2;
uint32_t new_lba = msg.args[0];
assert((unsigned int)max_ra < (SBSize / 4));
if(last_read_lba != ~0U && new_lba == (last_read_lba + 1))
{
int how_far_ahead = ra_lba - new_lba;
if(how_far_ahead <= max_ra)
ra_count = MIN(speedmult_ra, 1 + max_ra - how_far_ahead);
else
ra_count++;
}
else if(new_lba != last_read_lba)
{
ra_lba = new_lba;
ra_count = initial_ra;
}
last_read_lba = new_lba;
}
break;
}
}
// Don't read >= the "end" of the disc, silly snake. Slither.
if(ra_count && ra_lba == disc_toc.tracks[100].lba)
{
ra_count = 0;
//printf("Ephemeral scarabs: %d!\n", ra_lba);
}
if(ra_count)
{
uint8_t tmpbuf[2352 + 96];
bool error_condition = false;
disc_cdaccess->Read_Raw_Sector(tmpbuf, ra_lba);
slock_lock((slock_t*)SBMutex);
SectorBuffers[SBWritePos].lba = ra_lba;
memcpy(SectorBuffers[SBWritePos].data, tmpbuf, 2352 + 96);
SectorBuffers[SBWritePos].valid = true;
SectorBuffers[SBWritePos].error = error_condition;
SBWritePos = (SBWritePos + 1) % SBSize;
scond_signal((scond_t*)SBCond);
slock_unlock((slock_t*)SBMutex);
ra_lba++;
ra_count--;
}
}
return(1);
}
CDIF_MT::CDIF_MT(CDAccess *cda) : disc_cdaccess(cda), CDReadThread(NULL), SBMutex(NULL), SBCond(NULL)
{
CDIF_Message msg;
RTS_Args s;
SBMutex = slock_new();
SBCond = scond_new();
UnrecoverableError = false;
s.cdif_ptr = this;
CDReadThread = sthread_create((void (*)(void*))ReadThreadStart_C, &s);
EmuThreadQueue.Read(&msg);
}
CDIF_MT::~CDIF_MT()
{
bool thread_deaded_failed = false;
ReadThreadQueue.Write(CDIF_Message(CDIF_MSG_DIEDIEDIE));
if(!thread_deaded_failed)
sthread_join((sthread_t*)CDReadThread);
if(SBMutex)
{
slock_free((slock_t*)SBMutex);
SBMutex = NULL;
}
if(disc_cdaccess)
{
delete disc_cdaccess;
disc_cdaccess = NULL;
}
}
bool CDIF::ValidateRawSector(uint8 *buf)
bool CDIF::ValidateRawSector(uint8_t *buf)
{
int mode = buf[12 + 3];
@ -438,174 +132,91 @@ bool CDIF::ValidateRawSector(uint8 *buf)
return(true);
}
bool CDIF_MT::ReadRawSector(uint8 *buf, uint32 lba)
{
bool found = false;
bool error_condition = false;
if(UnrecoverableError)
{
memset(buf, 0, 2352 + 96);
return(false);
}
// This shouldn't happen, the emulated-system-specific CDROM emulation code should make sure the emulated program doesn't try
// to read past the last "real" sector of the disc.
if(lba >= disc_toc.tracks[100].lba)
{
printf("Attempt to read LBA %d, >= LBA %d\n", lba, disc_toc.tracks[100].lba);
return(false);
}
ReadThreadQueue.Write(CDIF_Message(CDIF_MSG_READ_SECTOR, lba));
slock_lock((slock_t*)SBMutex);
do
{
int i;
for(i = 0; i < SBSize; i++)
{
if(SectorBuffers[i].valid && SectorBuffers[i].lba == lba)
{
error_condition = SectorBuffers[i].error;
memcpy(buf, SectorBuffers[i].data, 2352 + 96);
found = true;
}
}
if(!found)
scond_wait((scond_t*)SBCond, (slock_t*)SBMutex);
} while(!found);
slock_unlock((slock_t*)SBMutex);
return(!error_condition);
}
bool CDIF_MT::ReadRawSectorPWOnly(uint8 *buf, uint32 lba, bool hint_fullread)
{
uint8 tmpbuf[2352 + 96];
bool ret;
if(UnrecoverableError)
{
memset(buf, 0, 96);
return(false);
}
// This shouldn't happen, the emulated-system-specific CDROM emulation code should make sure the emulated program doesn't try
// to read past the last "real" sector of the disc.
if(lba >= disc_toc.tracks[100].lba)
{
printf("Attempt to read LBA %d, >= LBA %d\n", lba, disc_toc.tracks[100].lba);
memset(buf, 0, 96);
return(false);
}
ret = ReadRawSector(tmpbuf, lba);
memcpy(buf, tmpbuf + 2352, 96);
return ret;
}
void CDIF_MT::HintReadSector(uint32 lba)
{
if(UnrecoverableError)
return;
ReadThreadQueue.Write(CDIF_Message(CDIF_MSG_READ_SECTOR, lba));
}
int CDIF::ReadSector(uint8* pBuf, uint32 lba, uint32 nSectors)
int CDIF::ReadSector(uint8_t* buf, int32_t lba, uint32_t sector_count, bool suppress_uncorrectable_message)
{
int ret = 0;
if(UnrecoverableError)
return(false);
while(nSectors--)
while(sector_count--)
{
int mode;
uint8_t tmpbuf[2352 + 96];
if(!ReadRawSector(tmpbuf, lba))
{
puts("CDIF Raw Read error");
return(false);
return false;
}
if(!ValidateRawSector(tmpbuf))
return(false);
{
if(!suppress_uncorrectable_message)
{
MDFN_DispMessage("Uncorrectable data at sector %d", lba);
log_cb(RETRO_LOG_ERROR, "Uncorrectable data at sector %d\n", lba);
}
mode = tmpbuf[12 + 3];
return(false);
}
const int mode = tmpbuf[12 + 3];
if(!ret)
ret = mode;
switch (mode)
if(mode == 1)
{
case 1:
memcpy(pBuf, &tmpbuf[12 + 4], 2048);
break;
case 2:
memcpy(pBuf, &tmpbuf[12 + 4 + 8], 2048);
break;
default:
printf("CDIF_ReadSector() invalid sector type at LBA=%u\n", (unsigned int)lba);
return(false);
memcpy(buf, &tmpbuf[12 + 4], 2048);
}
else if(mode == 2)
{
memcpy(buf, &tmpbuf[12 + 4 + 8], 2048);
}
else
{
printf("CDIF_ReadSector() invalid sector type at LBA=%u\n", (unsigned int)lba);
return(false);
}
pBuf += 2048;
buf += 2048;
lba++;
}
return(ret);
}
bool CDIF_MT::Eject(bool eject_status)
{
CDIF_Message msg;
if(UnrecoverableError)
return(false);
ReadThreadQueue.Write(CDIF_Message(CDIF_MSG_EJECT, eject_status));
EmuThreadQueue.Read(&msg);
return(true);
}
//
//
// Single-threaded implementation follows.
//
//
CDIF_ST::CDIF_ST(CDAccess *cda) : disc_cdaccess(cda)
{
//puts("***WARNING USING SINGLE-THREADED CD READER***");
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));
{
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)
void CDIF_ST::HintReadSector(int32_t lba)
{
/* TODO: disc_cdaccess seek hint? (probably not, would require asynchronousitycamel) */
// TODO: disc_cdaccess seek hint? (probably not, would require asynchronousitycamel)
}
bool CDIF_ST::ReadRawSector(uint8 *buf, uint32 lba)
bool CDIF_ST::ReadRawSector(uint8_t *buf, int32_t lba)
{
if(UnrecoverableError)
{
@ -613,208 +224,50 @@ bool CDIF_ST::ReadRawSector(uint8 *buf, uint32 lba)
return(false);
}
if(lba < LBA_Read_Minimum || lba > LBA_Read_Maximum)
{
printf("Attempt to read sector out of bounds; LBA=%d\n", lba);
memset(buf, 0, 2352 + 96);
return(false);
}
disc_cdaccess->Read_Raw_Sector(buf, lba);
return(true);
}
bool CDIF_ST::ReadRawSectorPWOnly(uint8 *buf, uint32 lba, bool hint_fullread)
{
uint8 tmpbuf[2352 + 96];
bool ret;
if(UnrecoverableError)
{
memset(buf, 0, 96);
return(false);
}
// This shouldn't happen, the emulated-system-specific CDROM emulation code should make sure the emulated program doesn't try
// to read past the last "real" sector of the disc.
if(lba >= disc_toc.tracks[100].lba)
{
printf("Attempt to read LBA %d, >= LBA %d\n", lba, disc_toc.tracks[100].lba);
memset(buf, 0, 96);
return(false);
}
ret = ReadRawSector(tmpbuf, lba);
memcpy(buf, tmpbuf + 2352, 96);
return ret;
}
bool CDIF_ST::Eject(bool eject_status)
bool CDIF_ST::ReadRawSectorPWOnly(uint8_t* pwbuf, int32_t lba, bool hint_fullread)
{
if(UnrecoverableError)
{
memset(pwbuf, 0, 96);
return(false);
int32_t old_de = DiscEjected;
DiscEjected = eject_status;
if(old_de != DiscEjected)
{
disc_cdaccess->Eject(eject_status);
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)
{
MDFN_Error(0, _("TOC first(%d)/last(%d) track numbers bad."), disc_toc.first_track, disc_toc.last_track);
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);
virtual uint8 *map(void);
virtual void unmap(void);
virtual uint64 read(void *data, uint64 count, bool error_on_eos = true);
virtual void write(const void *data, uint64 count);
virtual void seek(int64 offset, int whence);
virtual int64 tell(void);
virtual int64 size(void);
virtual void close(void);
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);
}
uint8 *CDIF_Stream_Thing::map(void)
{
return NULL;
}
void CDIF_Stream_Thing::unmap(void)
{
}
uint64 CDIF_Stream_Thing::read(void *data, uint64 count, bool error_on_eos)
{
uint64_t rp;
if(count > (((uint64)sector_count * 2048) - position))
if(lba < LBA_Read_Minimum || lba > LBA_Read_Maximum)
{
if(error_on_eos)
throw MDFN_Error(0, "EOF");
count = ((uint64)sector_count * 2048) - position;
printf("Attempt to read sector out of bounds; LBA=%d\n", lba);
memset(pwbuf, 0, 96);
return(false);
}
if(!count)
return(0);
for(rp = position; rp < (position + count); rp = (rp &~ 2047) + 2048)
if(disc_cdaccess->Fast_Read_Raw_PW_TSRE(pwbuf, lba))
return(true);
else
{
uint8_t buf[2048];
uint8_t tmpbuf[2352 + 96];
bool ret;
if(!cdintf->ReadSector(buf, start_lba + (rp / 2048), 1))
throw MDFN_Error(ErrnoHolder(EIO));
ret = ReadRawSector(tmpbuf, lba);
memcpy(pwbuf, tmpbuf + 2352, 96);
memcpy((uint8_t*)data + (rp - position),
buf + (rp & 2047),
std::min<uint64>(2048 - (rp & 2047),count - (rp - position))
);
return ret;
}
position += count;
return count;
}
void CDIF_Stream_Thing::write(const void *data, uint64 count)
CDIF *CDIF_Open(const std::string& path, bool image_memcache)
{
throw MDFN_Error(ErrnoHolder(EBADF));
}
void CDIF_Stream_Thing::seek(int64 offset, int whence)
{
int64 new_position;
switch(whence)
{
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;
}
int64 CDIF_Stream_Thing::tell(void)
{
return position;
}
int64 CDIF_Stream_Thing::size(void)
{
return(sector_count * 2048);
}
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(bool *success, const char *path, const bool is_device, bool image_memcache)
{
CDAccess *cda = cdaccess_open_image(success, path, image_memcache);
if(!image_memcache)
return new CDIF_MT(cda);
return new CDIF_ST(cda);
CDAccess *cda = CDAccess_Open(path, image_memcache);
return new CDIF_ST(cda);
}

View File

@ -19,50 +19,46 @@
#define __MDFN_CDROM_CDROMIF_H
#include "CDUtility.h"
#include "../Stream.h"
#include <mednafen/Stream.h>
#include <queue>
typedef TOC CD_TOC;
class CDIF
{
public:
public:
CDIF();
virtual ~CDIF();
CDIF();
virtual ~CDIF();
inline void ReadTOC(TOC *read_target)
{
*read_target = disc_toc;
}
static const int32_t LBA_Read_Minimum = -150;
static const int32_t LBA_Read_Maximum = 449849; // 100 * 75 * 60 - 150 - 1
virtual void HintReadSector(uint32_t lba) = 0;
virtual bool ReadRawSector(uint8_t *buf, uint32_t lba) = 0;
virtual bool ReadRawSectorPWOnly(uint8_t *buf, uint32_t lba, bool hint_fullread) = 0;
inline void ReadTOC(TOC *read_target)
{
*read_target = disc_toc;
}
// Call for mode 1 or mode 2 form 1 only.
bool ValidateRawSector(uint8_t *buf);
virtual void HintReadSector(int32_t lba) = 0;
virtual bool ReadRawSector(uint8_t *buf, int32_t lba) = 0; // Reads 2352+96 bytes of data into buf.
virtual bool ReadRawSectorPWOnly(uint8_t* pwbuf, int32_t lba, bool hint_fullread) = 0; // Reads 96 bytes(of raw subchannel PW data) into pwbuf.
// 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_t *pBuf, uint32_t lba, uint32_t nSectors);
// Call for mode 1 or mode 2 form 1 only.
bool ValidateRawSector(uint8_t *buf);
// 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;
// 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_t* buf, int32_t lba, uint32_t sector_count, bool suppress_uncorrectable_message = false);
// 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_t lba, uint32_t sector_count);
// 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(int32_t lba, uint32_t sector_count);
protected:
bool UnrecoverableError;
TOC disc_toc;
bool DiscEjected;
protected:
bool UnrecoverableError;
TOC disc_toc;
};
CDIF *CDIF_Open(bool *success, const char *path, const bool is_device, bool image_memcache);
CDIF *CDIF_Open(const std::string& path, bool image_memcache);
#endif

170
mednafen/cdrom/dvdisaster.h Normal file
View File

@ -0,0 +1,170 @@
/* 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 <stdint.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <math.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_t EDCCrc32(const unsigned char*, int);
/***
*** galois.c
***
* This is currently the hardcoded GF(2**8).
* int32_t 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_t gfGenerator; /* GF generator polynomial */
int32_t *indexOf; /* log */
int32_t *alphaTo; /* inverse log */
int32_t *encAlphaTo; /* inverse log optimized for encoder */
} GaloisTables;
/* Lookup and working tables for the ReedSolomon codecs */
typedef struct _ReedSolomonTables
{ GaloisTables *gfTables;/* from above */
int32_t *gpoly; /* RS code generator polynomial */
int32_t fcr; /* first consecutive root of RS generator polynomial */
int32_t primElem; /* primitive field element */
int32_t nroots; /* degree of RS generator polynomial */
int32_t ndata; /* data bytes per ecc block */
} ReedSolomonTables;
GaloisTables* CreateGaloisTables(int32_t);
void FreeGaloisTables(GaloisTables*);
ReedSolomonTables *CreateReedSolomonTables(GaloisTables*, int32_t, int32_t, 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_t uchar_to_int64_t(unsigned char*);
void int64_t_to_uchar(unsigned char*, int64_t);
void CalcSectors(int64_t, int64_t*, int*);
/***
*** recover-raw.c
***/
#define CD_RAW_SECTOR_SIZE 2352
#define CD_RAW_C2_SECTOR_SIZE (2352+294) /* main channel plus C2 vector */
int CheckEDC(const unsigned char*, bool);
int CheckMSF(unsigned char*, int);
int ValidateRawSector(unsigned char *frame, bool xaMode);
bool Init_LEC_Correct(void);
void Kill_LEC_Correct(void);
#endif /* DVDISASTER_H */

View File

@ -21,7 +21,7 @@
* or direct your browser at http://www.gnu.org.
*/
#include <stdint.h>
#include "dvdisaster.h"
/***
*** EDC checksum used in CDROM sectors
@ -47,7 +47,7 @@
/* */
/*****************************************************************/
unsigned long edctable[256] =
static const unsigned long edctable[256] =
{
0x00000000L, 0x90910101L, 0x91210201L, 0x01B00300L,
0x92410401L, 0x02D00500L, 0x03600600L, 0x93F10701L,
@ -120,11 +120,11 @@ unsigned long edctable[256] =
*/
uint32_t EDCCrc32(const unsigned char *data, int len)
{
uint32_t crc = 0;
{
uint32_t crc = 0;
while(len--)
crc = edctable[(crc ^ *data++) & 0xFF] ^ (crc >> 8);
while(len--)
crc = edctable[(crc ^ *data++) & 0xFF] ^ (crc >> 8);
return crc;
return crc;
}

View File

@ -1,16 +0,0 @@
#ifndef _EDC_CRC32_H
#define _EDC_CRC32_H
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
uint32_t EDCCrc32(const unsigned char*, int);
#ifdef __cplusplus
}
#endif
#endif

View File

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

View File

@ -22,9 +22,9 @@
* or direct your browser at http://www.gnu.org.
*/
#include <stdio.h>
#include <stdlib.h>
#include "galois.h"
#include "dvdisaster.h"
#include "galois-inlines.h"
/***
*** Galois field arithmetic.
@ -36,10 +36,11 @@
/* Initialize the Galois field tables */
GaloisTables* CreateGaloisTables(int32_t gf_generator)
{
int32_t b,log;
{
GaloisTables *gt = (GaloisTables *)calloc(1, sizeof(GaloisTables));
int32_t b,log;
/* Allocate the tables.
The encoder uses a special version of alpha_to which has the mod_fieldmax()
@ -50,7 +51,7 @@ GaloisTables* CreateGaloisTables(int32_t gf_generator)
gt->indexOf = (int32_t *)calloc(GF_FIELDSIZE, sizeof(int32_t));
gt->alphaTo = (int32_t *)calloc(GF_FIELDSIZE, sizeof(int32_t));
gt->encAlphaTo = (int32_t *)calloc(2*GF_FIELDSIZE, sizeof(int32_t));
/* create the log/ilog values */
for(b=1, log=0; log<GF_FIELDMAX; log++)
@ -58,13 +59,13 @@ GaloisTables* CreateGaloisTables(int32_t gf_generator)
gt->alphaTo[log] = b;
b = b << 1;
if(b & GF_FIELDSIZE)
b = b ^ gf_generator;
b = b ^ gf_generator;
}
if(b!=1)
{
printf("Failed to create the Galois field log tables!\n");
exit(1);
printf("Failed to create the Galois field log tables!\n");
exit(1);
}
/* we're even closed using infinity (makes things easier) */
@ -73,21 +74,18 @@ GaloisTables* CreateGaloisTables(int32_t gf_generator)
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)];
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);
if(gt->indexOf) free(gt->indexOf);
if(gt->alphaTo) free(gt->alphaTo);
if(gt->encAlphaTo) free(gt->encAlphaTo);
free(gt);
free(gt);
}
/***
@ -96,13 +94,11 @@ void FreeGaloisTables(GaloisTables *gt)
*/
ReedSolomonTables *CreateReedSolomonTables(GaloisTables *gt,
int32_t first_consecutive_root,
int32_t prim_elem,
int nroots_in)
{
int32_t first_consecutive_root,
int32_t prim_elem,
int nroots_in)
{ ReedSolomonTables *rt = (ReedSolomonTables *)calloc(1, sizeof(ReedSolomonTables));
int32_t i,j,root;
ReedSolomonTables *rt = (ReedSolomonTables *)
calloc(1, sizeof(ReedSolomonTables));
rt->gfTables = gt;
rt->fcr = first_consecutive_root;
@ -119,31 +115,42 @@ ReedSolomonTables *CreateReedSolomonTables(GaloisTables *gt,
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) */
/* 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];
}
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)];
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]];
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);
if(rt->gpoly) free(rt->gpoly);
free(rt);
free(rt);
}

View File

@ -1,76 +0,0 @@
#ifndef _GALOIS_H
#define _GALOIS_H
#include <stdint.h>
#include <retro_inline.h>
#ifdef __cplusplus
extern "C" {
#endif
/***
*** galois.c
***
* This is currently the hardcoded GF(2**8).
* int32_t 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_t gfGenerator; /* GF generator polynomial */
int32_t *indexOf; /* log */
int32_t *alphaTo; /* inverse log */
int32_t *encAlphaTo; /* inverse log optimized for encoder */
} GaloisTables;
/* Lookup and working tables for the ReedSolomon codecs */
typedef struct _ReedSolomonTables
{
GaloisTables *gfTables;/* from above */
int32_t *gpoly; /* RS code generator polynomial */
int32_t fcr; /* first consecutive root of RS generator polynomial */
int32_t primElem; /* primitive field element */
int32_t nroots; /* degree of RS generator polynomial */
int32_t ndata; /* data bytes per ecc block */
} ReedSolomonTables;
/*
* 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;
}
GaloisTables* CreateGaloisTables(int32_t a);
void FreeGaloisTables(GaloisTables *a);
ReedSolomonTables *CreateReedSolomonTables(GaloisTables *a, int32_t b, int32_t c, int d);
void FreeReedSolomonTables(ReedSolomonTables *a);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -22,9 +22,9 @@
* or direct your browser at http://www.gnu.org.
*/
#include "l-ec.h"
#include <stdlib.h>
#include <string.h>
#include "dvdisaster.h"
#include "galois-inlines.h"
#include <retro_miscellaneous.h>
@ -228,8 +228,7 @@ int CountC2Errors(unsigned char *frame)
int DecodePQ(ReedSolomonTables *rt, unsigned char *data, int padding,
int *erasure_list, int erasure_count)
{
GaloisTables *gt = rt->gfTables;
{ GaloisTables *gt = rt->gfTables;
int syndrome[NROOTS];
int lambda[NROOTS+1];
int omega[NROOTS+1];
@ -244,18 +243,18 @@ int DecodePQ(ReedSolomonTables *rt, unsigned char *data, int 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];
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)];
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. */
@ -268,7 +267,7 @@ int DecodePQ(ReedSolomonTables *rt, unsigned char *data, int padding,
/*** If the syndrome is zero, everything is fine. */
if(!syn_error)
return 0;
return 0;
/*** Initialize lambda to be the erasure locator polynomial */
@ -279,7 +278,7 @@ int DecodePQ(ReedSolomonTables *rt, unsigned char *data, int padding,
erasure_list[1] += padding;
if(erasure_count > 2) /* sanity check */
erasure_count = 0;
erasure_count = 0;
if(erasure_count > 0)
{ lambda[1] = gt->alphaTo[mod_fieldmax(LEC_PRIM_ELEM*(GF_FIELDMAX-1-erasure_list[0]))];
@ -288,17 +287,17 @@ int DecodePQ(ReedSolomonTables *rt, unsigned char *data, int padding,
{ 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]];
{ int tmp = gt->indexOf[lambda[j-1]];
if(tmp != GF_ALPHA0)
lambda[j] ^= gt->alphaTo[mod_fieldmax(u + tmp)];
}
lambda[j] ^= gt->alphaTo[mod_fieldmax(u + tmp)];
}
}
}
for(i=0; i<NROOTS+1; i++)
b[i] = gt->indexOf[lambda[i]];
b[i] = gt->indexOf[lambda[i]];
/*** Berlekamp-Massey algorithm to determine error+erasure locator polynomial */
r = erasure_count; /* r is the step number */
@ -310,42 +309,42 @@ int DecodePQ(ReedSolomonTables *rt, unsigned char *data, int padding,
{ 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])];
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;
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];
}
/* 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;
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;
}
/* 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]));
memcpy(lambda, t, (NROOTS+1)*sizeof(t[0]));
}
}
@ -355,7 +354,7 @@ int DecodePQ(ReedSolomonTables *rt, unsigned char *data, int padding,
for(i=0; i<NROOTS+1; i++)
{ lambda[i] = gt->indexOf[lambda[i]];
if(lambda[i] != GF_ALPHA0)
deg_lambda = i;
deg_lambda = i;
}
/*** Find roots of the error+erasure locator polynomial by Chien search */
@ -368,9 +367,9 @@ int DecodePQ(ReedSolomonTables *rt, unsigned char *data, int padding,
for(j=deg_lambda; j>0; j--)
{ if(reg[j] != GF_ALPHA0)
{ reg[j] = mod_fieldmax(reg[j] + j);
q ^= gt->alphaTo[reg[j]];
}
{ reg[j] = mod_fieldmax(reg[j] + j);
q ^= gt->alphaTo[reg[j]];
}
}
if(q != 0) continue; /* Not a root */
@ -402,7 +401,7 @@ int DecodePQ(ReedSolomonTables *rt, unsigned char *data, int padding,
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])];
tmp ^= gt->alphaTo[mod_fieldmax(syndrome[i - j] + lambda[j])];
}
omega[i] = gt->indexOf[tmp];
@ -421,54 +420,59 @@ int DecodePQ(ReedSolomonTables *rt, unsigned char *data, int padding,
for(i=deg_omega; i>=0; i--)
{ if(omega[i] != GF_ALPHA0)
num1 ^= gt->alphaTo[mod_fieldmax(omega[i] + i * root[j])];
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])];
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])];
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 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(!erasure_count)
erasure_list[0] = location-padding;
}
else
return -3;
#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];
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)];
}
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;
if(syndrome[i])
return -2;
#endif
return corrected;
}

View File

@ -1,45 +0,0 @@
#ifndef _L_EC_H
#define _L_EC_H
#include <stdint.h>
#include "galois.h"
#ifdef __cplusplus
extern "C" {
#endif
#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*);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -1,555 +0,0 @@
/* 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.
*/
#include <assert.h>
#include <sys/types.h>
#include <stdint.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
static uint8_t GF8_LOG[256];
static uint8_t GF8_ILOG[256];
uint16_t cf8_table[43][256];
uint32_t crc_table[256];
uint8_t scramble_table[2340];
/* Addition in the GF(8) domain: just the XOR of the values.
*/
#define gf8_add(a, b) (a) ^ (b)
/* Division in the GF(8) domain: Like multiplication but logarithms a
* subtracted.
*/
static uint8_t gf8_div(uint8_t a, uint8_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];
}
/* Reverses the bits in 'd'. 'bits' defines the bit width of 'd'.
*/
static uint32_t mirror_bits(uint32_t d, int bits)
{
int i;
uint32_t r = 0;
for (i = 0; i < bits; i++)
{
r <<= 1;
if ((d & 0x1) != 0)
r |= 0x1;
d >>= 1;
}
return r;
}
/* Calculates the CRC of given data with given lengths based on the
* table lookup algorithm.
*/
static uint32_t calc_edc(uint8_t *data, int len)
{
uint32_t crc = 0;
while (len--)
crc = crc_table[(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.
*/
static void scramble_table_init(void)
{
uint8_t d;
uint16_t i, j;
uint16_t reg = 1;
for (i = 0; i < 2340; i++)
{
d = 0;
for (j = 0; j < 8; j++)
{
d >>= 1;
if ((reg & 0x1) != 0)
d |= 0x80;
reg >>= 1;
if ((reg & 0x1) != ((reg >> 1) & 0x1))
reg |= 0x4000; /* 15-bit register */
}
scramble_table[i] = d;
}
}
/* 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).
*/
static void crc_table_init(void)
{
uint32_t i, j;
uint32_t r;
for (i = 0; i < 256; i++)
{
r = mirror_bits(i, 8);
r <<= 24;
for (j = 0; j < 8; j++)
{
r <<= 1;
if ((r & 0x80000000) != 0)
r ^= EDC_POLY;
}
r = mirror_bits(r, 32);
crc_table[i] = r;
}
}
/* Creates the logarithm and inverse logarithm table that is required
* for performing multiplication in the GF(8) domain.
*/
static void gf8_create_log_tables(void)
{
uint8_t log;
uint16_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[(uint8_t)b] = log;
GF8_ILOG[log] = (uint8_t)b;
b <<= 1;
if ((b & 0x100) != 0)
b ^= GF8_PRIM_POLY;
}
}
static void cf8_table_init(void)
{
int i, j;
uint16_t c;
uint8_t GF8_COEFFS_HELP[2][45];
uint8_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++)
{
cf8_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;
cf8_table[j][i] = GF8_ILOG[c];
c = GF8_LOG[i] + GF8_LOG[GF8_Q_COEFFS[1][j]];
if (c >= 255) c -= 255;
cf8_table[j][i] |= GF8_ILOG[c]<<8;
}
}
}
void lec_tables_init(void)
{
scramble_table_init();
crc_table_init();
cf8_table_init();
}
/* Calc EDC for a MODE 1 sector
*/
static void calc_mode1_edc(uint8_t *sector)
{
uint32_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(uint8_t *sector)
{
uint32_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(uint8_t *sector)
{
uint32_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(uint8_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 uint8_t bin2bcd(uint8_t b)
{
return (((b/10) << 4) & 0xf0) | ((b%10) & 0x0f);
}
/* Builds the sector header.
*/
static void set_sector_header(uint8_t mode, uint32_t adr, uint8_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(uint8_t *sector)
{
int i, j;
uint16_t p01_msb, p01_lsb;
uint8_t *p_lsb_start;
uint8_t *p_lsb;
uint8_t *p0, *p1;
uint8_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_table[j][d0];
p01_msb ^= cf8_table[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(uint8_t *sector)
{
int i, j;
uint16_t q01_lsb, q01_msb;
uint8_t *q_lsb_start;
uint8_t *q_lsb;
uint8_t *q0, *q1, *q_start;
uint8_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_table[j][d0];
q01_msb ^= cf8_table[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(uint32_t adr, uint8_t *sector)
{
uint16_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(uint32_t adr, uint8_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(uint32_t adr, uint8_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(uint32_t adr, uint8_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(uint32_t adr, uint8_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(uint8_t *sector)
{
uint16_t i;
uint8_t *p = sector;
uint8_t tmp;
const uint8_t *stable = scramble_table;
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;
}
}

688
mednafen/cdrom/lec.cpp Normal file
View File

@ -0,0 +1,688 @@
/* 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.
*/
#include <assert.h>
#include <stdint.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 uint8_t gf8_t;
static uint8_t GF8_LOG[256];
static gf8_t GF8_ILOG[256];
static const class Gf8_Q_Coeffs_Results_01 {
private:
uint16_t table[43][256];
public:
Gf8_Q_Coeffs_Results_01();
~Gf8_Q_Coeffs_Results_01() {}
const uint16_t *operator[] (int i) const { return &table[i][0]; }
operator const uint16_t *() const { return &table[0][0]; }
} CF8_Q_COEFFS_RESULTS_01;
static const class CrcTable {
private:
uint32_t table[256];
public:
CrcTable();
~CrcTable() {}
uint32_t operator[](int i) const { return table[i]; }
operator const uint32_t *() const { return table; }
} CRCTABLE;
static const class ScrambleTable {
private:
uint8_t table[2340];
public:
ScrambleTable();
~ScrambleTable() {}
uint8_t operator[](int i) const { return table[i]; }
operator const uint8_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()
{
uint8_t log;
uint16_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[(uint8_t)b] = log;
GF8_ILOG[log] = (uint8_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;
uint16_t c;
gf8_t GF8_COEFFS_HELP[2][45];
uint8_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 uint32_t mirror_bits(uint32_t d, int bits)
{
int i;
uint32_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 ()
{
uint32_t i, j;
uint32_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 uint32_t calc_edc(uint8_t *data, int len)
{
uint32_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()
{
uint16_t i, j;
uint16_t reg = 1;
uint8_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(uint8_t *sector)
{
uint32_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(uint8_t *sector)
{
uint32_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(uint8_t *sector)
{
uint32_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(uint8_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 uint8_t bin2bcd(uint8_t b)
{
return (((b/10) << 4) & 0xf0) | ((b%10) & 0x0f);
}
/* Builds the sector header.
*/
static void set_sector_header(uint8_t mode, uint32_t adr, uint8_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(uint8_t *sector)
{
int i, j;
uint16_t p01_msb, p01_lsb;
uint8_t *p_lsb_start;
uint8_t *p_lsb;
uint8_t *p0, *p1;
uint8_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(uint8_t *sector)
{
int i, j;
uint16_t q01_lsb, q01_msb;
uint8_t *q_lsb_start;
uint8_t *q_lsb;
uint8_t *q0, *q1, *q_start;
uint8_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(uint32_t adr, uint8_t *sector)
{
uint16_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(uint32_t adr, uint8_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(uint32_t adr, uint8_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(uint32_t adr, uint8_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(uint32_t adr, uint8_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(uint8_t *sector)
{
uint16_t i;
const uint8_t *stable = SCRAMBLE_TABLE;
uint8_t *p = sector;
uint8_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;
uint8_t buffer1[2352];
uint8_t buffer2[2352];
uint32_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

View File

@ -21,15 +21,6 @@
#define __LEC_H__
#include <stdint.h>
#include <boolean.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifndef TRUE
#define TRUE 1
#endif
/* Encodes a MODE 0 sector.
* 'adr' is the current physical sector address
@ -68,12 +59,6 @@ void lec_encode_mode2_form2_sector(uint32_t adr, uint8_t *sector);
/* Scrambles and byte swaps an encoded sector.
* 'sector' must be 2352 byte wide.
*/
void lec_scramble(uint8_t *sector);
void lec_tables_init(void);
#ifdef __cplusplus
}
#endif
void lec_scramble(int8_t *sector);
#endif

View File

@ -1,28 +0,0 @@
#include <stdint.h>
#include <string.h>
#include <string>
#include "misc.h"
void MDFN_strtoupper(char *str)
{
size_t x;
for(x = 0; str[x]; x++)
{
if(str[x] >= 'a' && str[x] <= 'z')
str[x] = str[x] - 'a' + 'A';
}
}
void MDFN_strtoupper(std::string &str)
{
size_t x;
const size_t len = str.length();
for(x = 0; x < len; x++)
{
if(str[x] >= 'a' && str[x] <= 'z')
str[x] = str[x] - 'a' + 'A';
}
}

View File

@ -1,7 +0,0 @@
#ifndef __MDFN_CDROM_MISC_H
#define __MDFN_CDROM_MISC_H
void MDFN_strtoupper(std::string &str);
void MDFN_strtoupper(char *str);
#endif

View File

@ -19,28 +19,24 @@
* or direct your browser at http://www.gnu.org.
*/
#include <stdint.h>
#include <string.h>
#include "recover-raw.h"
#include "l-ec.h"
#include "edc_crc32.h"
#include "galois.h"
#include <boolean.h>
#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);
gt = CreateGaloisTables(0x11d);
rt = CreateReedSolomonTables(gt, 0, 1, 10);
return(1);
return(1);
}
void Kill_LEC_Correct(void)
{
FreeGaloisTables(gt);
FreeReedSolomonTables(rt);
FreeGaloisTables(gt);
FreeReedSolomonTables(rt);
}
/***
@ -49,29 +45,31 @@ void Kill_LEC_Correct(void)
/*
* Test raw sector against its 32bit CRC.
* Returns TRUE if frame is good.
* 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;
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;
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(xa_mode)
real_crc = EDCCrc32(cd_frame+16, 2056);
else
real_crc = EDCCrc32(cd_frame, 2064);
if(expected_crc == real_crc)
return(1);
//printf("Bad EDC CRC: Calculated: %08x, Recorded: %08x\n", real_crc, expected_crc);
return(0);
if(expected_crc == real_crc)
return(1);
else
{
//printf("Bad EDC CRC: Calculated: %08x, Recorded: %08x\n", real_crc, expected_crc);
return(0);
}
}
/***
@ -104,34 +102,31 @@ static int simple_lec(unsigned char *frame)
/* Perform Q-Parity error correction */
for(q=0; q<N_Q_VECTORS; q++)
{
int err;
{ int err;
/* We have no erasure information for Q vectors */
GetQVector(frame, q_vector, q);
err = DecodePQ(rt, q_vector, Q_PADDING, ignore, 0);
GetQVector(frame, q_vector, q);
err = DecodePQ(rt, q_vector, Q_PADDING, ignore, 0);
/* See what we've got */
/* 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++;
}
}
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;
{ int err,i;
/* Try error correction without erasure information */
@ -139,41 +134,41 @@ static int simple_lec(unsigned char *frame)
err = DecodePQ(rt, p_vector, P_PADDING, ignore, 0);
/* If unsuccessful, try again using erasures.
Erasure information is uncertain, so try this last. */
Erasure information is uncertain, so try this last. */
if(err < 0 || err > 2)
{
GetPVector(byte_state, p_state, p);
erasure_count = 0;
{ 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;
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);
}
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++;
{ p_failures++;
}
else /* Correctable. */
{
if(err == 1 || err == 2) /* Store back corrected vector */
{ SetPVector(frame, p_vector, p);
p_corrected++;
}
{ 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 1;
}
return 0;
}
@ -183,25 +178,36 @@ static int simple_lec(unsigned char *frame)
***/
int ValidateRawSector(unsigned char *frame, bool xaMode)
{
int lec_did_sth = false;
{
int lec_did_sth = false;
// Silence GCC warning
(void)lec_did_sth;
/* 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. */
/* 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))
{
unsigned char header[4];
if(!CheckEDC(frame, xaMode))
lec_did_sth = simple_lec(frame);
/* Test internal sector checksum again */
if(xaMode)
{
memcpy(header, frame + 12, 4);
memset(frame + 12, 0, 4);
}
lec_did_sth = simple_lec(frame);
if(xaMode)
memcpy(frame + 12, header, 4);
}
/* Test internal sector checksum again */
if(!CheckEDC(frame, xaMode))
/* EDC failure in RAW sector */
if(!CheckEDC(frame, xaMode))
return false;
return false;
return true;
return true;
}

View File

@ -1,26 +0,0 @@
#ifndef _RECOVER_RAW_H
#define _RECOVER_RAW_H
#include <stdint.h>
#include <boolean.h>
#ifdef __cplusplus
extern "C" {
#endif
#define CD_RAW_SECTOR_SIZE 2352
#define CD_RAW_C2_SECTOR_SIZE (2352+294) /* main channel plus C2 vector */
int CheckEDC(const unsigned char *a, bool b);
int CheckMSF(unsigned char *a, int b);
int ValidateRawSector(unsigned char *frame, bool xaMode);
bool Init_LEC_Correct(void);
void Kill_LEC_Correct(void);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,259 @@
/********************************************************
* *
* PC Engine CD Command 0xD8 - SAPSP *
* *
********************************************************/
static void DoNEC_PCE_SAPSP(const uint8_t *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_t *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_t *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_t *cdb)
{
uint8_t *SubQBuf = cd.SubQBuf[QMode_Time];
uint8_t 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_t *cdb)
{
// Problems:
// Returned data lengths on real PCE are not confirmed.
// Mode 0x03 behavior not tested on real PCE
uint8_t 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_t 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_t 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);
}

3243
mednafen/cdrom/scsicd.cpp Normal file

File diff suppressed because it is too large Load Diff

99
mednafen/cdrom/scsicd.h Normal file
View File

@ -0,0 +1,99 @@
#ifndef __PCFX_SCSICD_H
#define __PCFX_SCSICD_H
typedef int32_t 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_t DB;
uint32_t 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_t)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_t 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_t SCSICD_Run(scsicd_timestamp_t);
void SCSICD_ResetTS(uint32_t 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_t* left_hrbuf, int32_t* right_hrbuf, uint32_t TransferRate, uint32_t SystemClock, void (*IRQFunc)(int), void (*SSCFunc)(uint8_t, int));
void SCSICD_Close(void);
void SCSICD_SetTransferRate(uint32_t TransferRate);
void SCSICD_SetCDDAVolume(double left, double right);
void SCSICD_StateAction(StateMem *sm, const unsigned load, const bool data_only, const char *sname);
void SCSICD_SetDisc(bool tray_open, CDIF *cdif, bool no_emu_side_effects = false);
#endif

View File

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

View File

@ -4,6 +4,7 @@
#include <algorithm>
#include <string>
#include <vector>
#include "../libretro.h"
#include "video.h"
#include "state.h"
@ -468,4 +469,6 @@ typedef struct
int StateAction(StateMem *sm, int load, int data_only);
extern retro_log_printf_t log_cb;
#endif