mirror of
https://github.com/libretro/beetle-saturn-libretro.git
synced 2024-11-27 11:20:39 +00:00
Update to new cdrom code
This commit is contained in:
parent
cb94564349
commit
69eef0ee17
@ -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 \
|
||||
|
12
libretro.cpp
12
libretro.cpp
@ -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);
|
||||
|
60
mednafen/cdrom/CDAFReader.cpp
Normal file
60
mednafen/cdrom/CDAFReader.cpp
Normal 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);
|
||||
}
|
||||
|
62
mednafen/cdrom/CDAFReader.h
Normal file
62
mednafen/cdrom/CDAFReader.h
Normal 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
|
203
mednafen/cdrom/CDAFReader_MPC.cpp
Normal file
203
mednafen/cdrom/CDAFReader_MPC.cpp
Normal 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);
|
||||
}
|
27
mednafen/cdrom/CDAFReader_MPC.h
Normal file
27
mednafen/cdrom/CDAFReader_MPC.h
Normal 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
|
129
mednafen/cdrom/CDAFReader_Vorbis.cpp
Normal file
129
mednafen/cdrom/CDAFReader_Vorbis.cpp
Normal 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);
|
||||
}
|
28
mednafen/cdrom/CDAFReader_Vorbis.h
Normal file
28
mednafen/cdrom/CDAFReader_Vorbis.h
Normal 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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -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
@ -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);
|
||||
};
|
||||
|
||||
|
@ -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];
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
@ -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
|
@ -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);
|
||||
}
|
||||
|
@ -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
170
mednafen/cdrom/dvdisaster.h
Normal 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 */
|
@ -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;
|
||||
}
|
@ -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
|
40
mednafen/cdrom/galois-inlines.h
Normal file
40
mednafen/cdrom/galois-inlines.h
Normal 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;
|
||||
}
|
@ -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);
|
||||
}
|
@ -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
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
@ -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
688
mednafen/cdrom/lec.cpp
Normal 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
|
@ -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
|
||||
|
@ -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';
|
||||
}
|
||||
}
|
@ -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
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
259
mednafen/cdrom/scsicd-pce-commands.inc
Normal file
259
mednafen/cdrom/scsicd-pce-commands.inc
Normal 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
3243
mednafen/cdrom/scsicd.cpp
Normal file
File diff suppressed because it is too large
Load Diff
99
mednafen/cdrom/scsicd.h
Normal file
99
mednafen/cdrom/scsicd.h
Normal 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
|
69
mednafen/cdrom/scsicd_cdda_filter.inc
Normal file
69
mednafen/cdrom/scsicd_cdda_filter.inc
Normal 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)
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user