update mednadisc to 0.9.38.4
This commit is contained in:
parent
bea654bced
commit
fada87b3d3
|
@ -25,7 +25,7 @@ EW_EXPORT void* mednadisc_LoadCD(const char* fname)
|
|||
{
|
||||
CDAccess* disc = NULL;
|
||||
try {
|
||||
disc = cdaccess_open_image(fname,false);
|
||||
disc = CDAccess_Open(fname,false);
|
||||
}
|
||||
catch(MDFN_Error &) {
|
||||
return NULL;
|
||||
|
@ -53,6 +53,9 @@ EW_EXPORT void mednadisc_ReadTOC(MednaDisc* md, JustTOC* justToc, CDUtility::TOC
|
|||
memcpy(tracks101,toc.tracks,sizeof(toc.tracks));
|
||||
}
|
||||
|
||||
//NOTE: the subcode will come out interleaved.
|
||||
//this is almost useless, but it won't always be needed, so we're not deinterleaving it here yet
|
||||
//we should probably have more granular control than just reading this one sector eventually
|
||||
EW_EXPORT int32 mednadisc_ReadSector(MednaDisc* md, int lba, void* buf2448)
|
||||
{
|
||||
CDAccess* disc = md->disc;
|
||||
|
|
|
@ -1,248 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="psx">
|
||||
<UniqueIdentifier>{00f73db4-1182-4bf7-b891-66bf860d3742}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="emuware">
|
||||
<UniqueIdentifier>{f69cc8f2-7480-44d6-9a32-9dca789d2bf6}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="cdrom">
|
||||
<UniqueIdentifier>{57a8e6ec-9225-410d-b38f-ba209abae070}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="psx\input">
|
||||
<UniqueIdentifier>{76abb796-5411-4d33-b3e0-f1f3873f138e}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="emuware\msvc">
|
||||
<UniqueIdentifier>{cb700979-4dce-4b10-8521-3ab71a313271}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="video">
|
||||
<UniqueIdentifier>{d1f71901-17a5-441a-8b4f-f7da34a057c1}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\psx\cdc.cpp">
|
||||
<Filter>psx</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\psx\psx.cpp">
|
||||
<Filter>psx</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\psx\dis.cpp">
|
||||
<Filter>psx</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\psx\gte.cpp">
|
||||
<Filter>psx</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\psx\spu.cpp">
|
||||
<Filter>psx</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\Stream.cpp" />
|
||||
<ClCompile Include="..\psx\frontio.cpp">
|
||||
<Filter>psx</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\psx\cpu.cpp">
|
||||
<Filter>psx</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\psx\dma.cpp">
|
||||
<Filter>psx</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\psx\irq.cpp">
|
||||
<Filter>psx</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\psx\mdec.cpp">
|
||||
<Filter>psx</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\psx\sio.cpp">
|
||||
<Filter>psx</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\psx\timer.cpp">
|
||||
<Filter>psx</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\endian.cpp" />
|
||||
<ClCompile Include="..\psx\input\dualshock.cpp">
|
||||
<Filter>psx\input</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\psx\input\gamepad.cpp">
|
||||
<Filter>psx\input</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\psx\input\guncon.cpp">
|
||||
<Filter>psx\input</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\psx\input\justifier.cpp">
|
||||
<Filter>psx\input</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\psx\input\memcard.cpp">
|
||||
<Filter>psx\input</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\psx\input\mouse.cpp">
|
||||
<Filter>psx\input</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\psx\input\multitap.cpp">
|
||||
<Filter>psx\input</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\psx\input\negcon.cpp">
|
||||
<Filter>psx\input</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\psx\input\dualanalog.cpp">
|
||||
<Filter>psx\input</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\octoshock.cpp" />
|
||||
<ClCompile Include="..\emuware\emuware.cpp">
|
||||
<Filter>emuware</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\video\surface.cpp">
|
||||
<Filter>video</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\video\Deinterlacer.cpp">
|
||||
<Filter>video</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\emuware\EW_state.cpp">
|
||||
<Filter>emuware</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\psx\gpu.cpp">
|
||||
<Filter>psx</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\psx\gpu_line.cpp">
|
||||
<Filter>psx</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\psx\gpu_polygon.cpp">
|
||||
<Filter>psx</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\psx\gpu_sprite.cpp">
|
||||
<Filter>psx</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\cdrom\CDUtility.cpp">
|
||||
<Filter>cdrom</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\psx\cdc.h">
|
||||
<Filter>psx</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\psx\psx.h">
|
||||
<Filter>psx</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\emuware\emuware.h">
|
||||
<Filter>emuware</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\psx\dis.h">
|
||||
<Filter>psx</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\psx\gte.h">
|
||||
<Filter>psx</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\math_ops.h" />
|
||||
<ClInclude Include="..\git.h" />
|
||||
<ClInclude Include="..\octoshock.h" />
|
||||
<ClInclude Include="..\psx\spu.h">
|
||||
<Filter>psx</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\endian.h" />
|
||||
<ClInclude Include="..\Stream.h" />
|
||||
<ClInclude Include="..\error.h" />
|
||||
<ClInclude Include="..\psx\frontio.h">
|
||||
<Filter>psx</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\psx\cpu.h">
|
||||
<Filter>psx</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\psx\dma.h">
|
||||
<Filter>psx</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\psx\irq.h">
|
||||
<Filter>psx</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\psx\mdec.h">
|
||||
<Filter>psx</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\psx\sio.h">
|
||||
<Filter>psx</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\psx\timer.h">
|
||||
<Filter>psx</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\psx\input\dualanalog.h">
|
||||
<Filter>psx\input</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\psx\input\dualshock.h">
|
||||
<Filter>psx\input</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\psx\input\gamepad.h">
|
||||
<Filter>psx\input</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\psx\input\guncon.h">
|
||||
<Filter>psx\input</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\psx\input\justifier.h">
|
||||
<Filter>psx\input</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\psx\input\memcard.h">
|
||||
<Filter>psx\input</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\psx\input\mouse.h">
|
||||
<Filter>psx\input</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\psx\input\multitap.h">
|
||||
<Filter>psx\input</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\psx\input\negcon.h">
|
||||
<Filter>psx\input</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\emuware\msvc\inttypes.h">
|
||||
<Filter>emuware\msvc</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\emuware\msvc\stdint.h">
|
||||
<Filter>emuware\msvc</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\video\surface.h">
|
||||
<Filter>video</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\video\Deinterlacer.h">
|
||||
<Filter>video</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\emuware\EW_state.h">
|
||||
<Filter>emuware</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\cdrom\SimpleFIFO.h">
|
||||
<Filter>cdrom</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\psx\gpu.h">
|
||||
<Filter>psx</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\psx\masmem.h">
|
||||
<Filter>psx</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\cdrom\CDUtility.h">
|
||||
<Filter>cdrom</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\psx\spu_fir_table.inc">
|
||||
<Filter>psx</Filter>
|
||||
</None>
|
||||
<None Include="..\psx\spu_reverb.inc">
|
||||
<Filter>psx</Filter>
|
||||
</None>
|
||||
<None Include="..\psx\gpu_command_table.inc">
|
||||
<Filter>psx</Filter>
|
||||
</None>
|
||||
<None Include="..\psx\gpu_common.inc">
|
||||
<Filter>psx</Filter>
|
||||
</None>
|
||||
<None Include="..\psx\gpu_line.inc">
|
||||
<Filter>psx</Filter>
|
||||
</None>
|
||||
<None Include="..\psx\gpu_polygon.inc">
|
||||
<Filter>psx</Filter>
|
||||
</None>
|
||||
<None Include="..\psx\gpu_sprite.inc">
|
||||
<Filter>psx</Filter>
|
||||
</None>
|
||||
<None Include="..\psx\cpu_computedgoto.inc">
|
||||
<Filter>psx</Filter>
|
||||
</None>
|
||||
<None Include="..\psx\cpu_bigswitch.inc">
|
||||
<Filter>psx</Filter>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -11,10 +11,10 @@
|
|||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\cdrom\audioreader.cpp" />
|
||||
<ClCompile Include="..\cdrom\CDAccess.cpp" />
|
||||
<ClCompile Include="..\cdrom\CDAccess_CCD.cpp" />
|
||||
<ClCompile Include="..\cdrom\CDAccess_Image.cpp" />
|
||||
<ClCompile Include="..\cdrom\CDAFReader.cpp" />
|
||||
<ClCompile Include="..\cdrom\cdromif.cpp" />
|
||||
<ClCompile Include="..\cdrom\CDUtility.cpp" />
|
||||
<ClCompile Include="..\cdrom\crc32.cpp" />
|
||||
|
@ -30,12 +30,15 @@
|
|||
<ClCompile Include="..\MemoryStream.cpp" />
|
||||
<ClCompile Include="..\Stream.cpp" />
|
||||
<ClCompile Include="..\string\trim.cpp" />
|
||||
<ClCompile Include="..\trio\trio.c" />
|
||||
<ClCompile Include="..\trio\trionan.c" />
|
||||
<ClCompile Include="..\trio\triostr.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\cdrom\audioreader.h" />
|
||||
<ClInclude Include="..\cdrom\CDAccess.h" />
|
||||
<ClInclude Include="..\cdrom\CDAccess_CCD.h" />
|
||||
<ClInclude Include="..\cdrom\CDAccess_Image.h" />
|
||||
<ClInclude Include="..\cdrom\CDAFReader.h" />
|
||||
<ClInclude Include="..\cdrom\cdromif.h" />
|
||||
<ClInclude Include="..\cdrom\CDUtility.h" />
|
||||
<ClInclude Include="..\cdrom\dvdisaster.h" />
|
||||
|
@ -51,6 +54,7 @@
|
|||
<ClInclude Include="..\MemoryStream.h" />
|
||||
<ClInclude Include="..\Stream.h" />
|
||||
<ClInclude Include="..\string\trim.h" />
|
||||
<ClInclude Include="..\trio\trio.h" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{5F35CAFC-6208-4FBE-AD17-0E69BA3F70EC}</ProjectGuid>
|
||||
|
@ -81,7 +85,7 @@
|
|||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<OutDir>$(ProjectDir)\..\..\..\output\dll\</OutDir>
|
||||
<OutDir>$(ProjectDir)..\..\..\output\dll\</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
|
@ -92,7 +96,7 @@
|
|||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>EW_EXPORT;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_WINDOWS;_USRDLL;OCTOSHOCK_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>TRIO_PUBLIC=;TRIO_PRIVATE=static;EW_EXPORT;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_WINDOWS;_USRDLL;OCTOSHOCK_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>../emuware/msvc;..</AdditionalIncludeDirectories>
|
||||
<PrecompiledHeaderFile>
|
||||
</PrecompiledHeaderFile>
|
||||
|
|
|
@ -10,6 +10,9 @@
|
|||
<Filter Include="string">
|
||||
<UniqueIdentifier>{798fa5bd-6381-487a-99d2-35a15a6da439}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="trio">
|
||||
<UniqueIdentifier>{a43930f5-41a5-4b2b-92ef-bd90f9716127}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\cdrom\CDAccess_Image.cpp">
|
||||
|
@ -51,10 +54,19 @@
|
|||
<Filter>string</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\general.cpp" />
|
||||
<ClCompile Include="..\cdrom\audioreader.cpp">
|
||||
<ClCompile Include="..\Mednadisc.cpp" />
|
||||
<ClCompile Include="..\trio\trio.c">
|
||||
<Filter>trio</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\cdrom\CDAFReader.cpp">
|
||||
<Filter>cdrom</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\Mednadisc.cpp" />
|
||||
<ClCompile Include="..\trio\trionan.c">
|
||||
<Filter>trio</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\trio\triostr.c">
|
||||
<Filter>trio</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\cdrom\CDAccess_Image.h">
|
||||
|
@ -96,9 +108,12 @@
|
|||
<Filter>string</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\general.h" />
|
||||
<ClInclude Include="..\cdrom\audioreader.h">
|
||||
<ClInclude Include="..\Mednadisc.h" />
|
||||
<ClInclude Include="..\trio\trio.h">
|
||||
<Filter>trio</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\cdrom\CDAFReader.h">
|
||||
<Filter>cdrom</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Mednadisc.h" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,83 @@
|
|||
/* 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
|
||||
*/
|
||||
|
||||
// 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 "emuware/emuware.h"
|
||||
#include "CDAFReader.h"
|
||||
#include "CDAFReader_Vorbis.h"
|
||||
#include "CDAFReader_MPC.h"
|
||||
|
||||
#ifdef HAVE_LIBSNDFILE
|
||||
#include "CDAFReader_SF.h"
|
||||
#endif
|
||||
|
||||
CDAFReader::CDAFReader() : LastReadPos(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CDAFReader::~CDAFReader()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CDAFReader* CDAFR_Null_Open(Stream* fp)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CDAFReader *CDAFR_Open(Stream *fp)
|
||||
{
|
||||
static CDAFReader* (* const OpenFuncs[])(Stream* fp) =
|
||||
{
|
||||
#ifdef HAVE_MPC
|
||||
CDAFR_MPC_Open,
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_VORBIS
|
||||
CDAFR_Vorbis_Open, // Must come before CDAFR_SF_Open
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LIBSNDFILE
|
||||
CDAFR_SF_Open,
|
||||
#endif
|
||||
|
||||
CDAFR_Null_Open
|
||||
};
|
||||
|
||||
for(int idx=0;idx<ARRAY_SIZE(OpenFuncs);idx++)
|
||||
{
|
||||
auto f = OpenFuncs[idx];
|
||||
try
|
||||
{
|
||||
fp->rewind();
|
||||
return f(fp);
|
||||
}
|
||||
catch(int i)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return(NULL);
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
#ifndef __MDFN_CDAFREADER_H
|
||||
#define __MDFN_CDAFREADER_H
|
||||
|
||||
#include "Stream.h"
|
||||
|
||||
class CDAFReader
|
||||
{
|
||||
public:
|
||||
CDAFReader();
|
||||
virtual ~CDAFReader();
|
||||
|
||||
virtual uint64 FrameCount(void) = 0;
|
||||
INLINE uint64 Read(uint64 frame_offset, int16 *buffer, uint64 frames)
|
||||
{
|
||||
uint64 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 Read_(int16 *buffer, uint64 frames) = 0;
|
||||
virtual bool Seek_(uint64 frame_offset) = 0;
|
||||
|
||||
uint64 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
|
|
@ -0,0 +1,238 @@
|
|||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <mednafen/mednafen.h>
|
||||
#include "CDAFReader.h"
|
||||
#include "CDAFReader_MPC.h"
|
||||
|
||||
#if 0
|
||||
#include <mpc/mpcdec.h>
|
||||
#else
|
||||
#include <mednafen/mpcdec/mpcdec.h>
|
||||
#endif
|
||||
|
||||
class CDAFReader_MPC final : public CDAFReader
|
||||
{
|
||||
public:
|
||||
CDAFReader_MPC(Stream *fp);
|
||||
~CDAFReader_MPC();
|
||||
|
||||
uint64 Read_(int16 *buffer, uint64 frames) override;
|
||||
bool Seek_(uint64 frame_offset) override;
|
||||
uint64 FrameCount(void) override;
|
||||
|
||||
private:
|
||||
mpc_reader reader;
|
||||
mpc_demux *demux;
|
||||
mpc_streaminfo si;
|
||||
|
||||
MPC_SAMPLE_FORMAT MPCBuffer[MPC_DECODER_BUFFER_LENGTH];
|
||||
|
||||
uint32 MPCBufferIn;
|
||||
uint32 MPCBufferOffs;
|
||||
Stream *fw;
|
||||
};
|
||||
|
||||
|
||||
/// Reads size bytes of data into buffer at ptr.
|
||||
static mpc_int32_t impc_read(mpc_reader *p_reader, void *ptr, mpc_int32_t size)
|
||||
{
|
||||
Stream *fw = (Stream*)(p_reader->data);
|
||||
|
||||
try
|
||||
{
|
||||
return fw->read(ptr, size, false);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return(MPC_STATUS_FAIL);
|
||||
}
|
||||
}
|
||||
|
||||
/// Seeks to byte position offset.
|
||||
static mpc_bool_t impc_seek(mpc_reader *p_reader, mpc_int32_t offset)
|
||||
{
|
||||
Stream *fw = (Stream*)(p_reader->data);
|
||||
|
||||
try
|
||||
{
|
||||
fw->seek(offset, SEEK_SET);
|
||||
return(MPC_TRUE);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return(MPC_FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the current byte offset in the stream.
|
||||
static mpc_int32_t impc_tell(mpc_reader *p_reader)
|
||||
{
|
||||
Stream *fw = (Stream*)(p_reader->data);
|
||||
|
||||
try
|
||||
{
|
||||
return fw->tell();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return(MPC_STATUS_FAIL);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the total length of the source stream, in bytes.
|
||||
static mpc_int32_t impc_get_size(mpc_reader *p_reader)
|
||||
{
|
||||
Stream *fw = (Stream*)(p_reader->data);
|
||||
|
||||
try
|
||||
{
|
||||
return fw->size();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return(MPC_STATUS_FAIL);
|
||||
}
|
||||
}
|
||||
|
||||
/// True if the stream is a seekable stream.
|
||||
static mpc_bool_t impc_canseek(mpc_reader *p_reader)
|
||||
{
|
||||
return(MPC_TRUE);
|
||||
}
|
||||
|
||||
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 CDAFReader_MPC::Read_(int16 *buffer, uint64 frames)
|
||||
{
|
||||
mpc_status err;
|
||||
int16 *cowbuf = (int16 *)buffer;
|
||||
int32 toread = frames * 2;
|
||||
|
||||
while(toread > 0)
|
||||
{
|
||||
int32 tmplen;
|
||||
|
||||
if(!MPCBufferIn)
|
||||
{
|
||||
mpc_frame_info fi;
|
||||
memset(&fi, 0, sizeof(fi));
|
||||
|
||||
fi.buffer = MPCBuffer;
|
||||
if((err = mpc_demux_decode(demux, &fi)) < 0 || fi.bits == -1)
|
||||
return(frames - toread / 2);
|
||||
|
||||
MPCBufferIn = fi.samples * 2;
|
||||
MPCBufferOffs = 0;
|
||||
}
|
||||
|
||||
tmplen = MPCBufferIn;
|
||||
|
||||
if(tmplen >= toread)
|
||||
tmplen = toread;
|
||||
|
||||
for(int x = 0; x < tmplen; x++)
|
||||
{
|
||||
#ifdef MPC_FIXED_POINT
|
||||
int32 samp = MPCBuffer[MPCBufferOffs + x] >> MPC_FIXED_POINT_FRACTPART;
|
||||
#else
|
||||
#warning Floating-point MPC decoding path not tested.
|
||||
int32 samp = (int32)(MPCBuffer[MPCBufferOffs + x] * 32767);
|
||||
#endif
|
||||
if(samp < -32768)
|
||||
samp = -32768;
|
||||
|
||||
if(samp > 32767)
|
||||
samp = 32767;
|
||||
|
||||
*cowbuf = (int16)samp;
|
||||
cowbuf++;
|
||||
}
|
||||
|
||||
MPCBufferOffs += tmplen;
|
||||
toread -= tmplen;
|
||||
MPCBufferIn -= tmplen;
|
||||
}
|
||||
|
||||
return(frames - toread / 2);
|
||||
}
|
||||
|
||||
bool CDAFReader_MPC::Seek_(uint64 frame_offset)
|
||||
{
|
||||
MPCBufferOffs = 0;
|
||||
MPCBufferIn = 0;
|
||||
|
||||
if(mpc_demux_seek_sample(demux, frame_offset) < 0)
|
||||
return(false);
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
uint64 CDAFReader_MPC::FrameCount(void)
|
||||
{
|
||||
return(mpc_streaminfo_get_length_samples(&si));
|
||||
}
|
||||
|
||||
|
||||
CDAFReader* CDAFR_MPC_Open(Stream* fp)
|
||||
{
|
||||
return new CDAFReader_MPC(fp);
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
#ifndef __MDFN_CDAFREADER_MPC_H
|
||||
#define __MDFN_CDAFREADER_MPC_H
|
||||
|
||||
CDAFReader* CDAFR_MPC_Open(Stream* fp);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,155 @@
|
|||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <mednafen/mednafen.h>
|
||||
#include "CDAFReader.h"
|
||||
#include "CDAFReader_SF.h"
|
||||
|
||||
#include <sndfile.h>
|
||||
|
||||
class CDAFReader_SF final : public CDAFReader
|
||||
{
|
||||
public:
|
||||
|
||||
CDAFReader_SF(Stream *fp);
|
||||
~CDAFReader_SF();
|
||||
|
||||
uint64 Read_(int16 *buffer, uint64 frames) override;
|
||||
bool Seek_(uint64 frame_offset) override;
|
||||
uint64 FrameCount(void) override;
|
||||
|
||||
private:
|
||||
SNDFILE *sf;
|
||||
SF_INFO sfinfo;
|
||||
SF_VIRTUAL_IO sfvf;
|
||||
|
||||
Stream *fw;
|
||||
};
|
||||
|
||||
static sf_count_t isf_get_filelen(void *user_data)
|
||||
{
|
||||
Stream *fw = (Stream*)user_data;
|
||||
|
||||
try
|
||||
{
|
||||
return fw->size();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return(-1);
|
||||
}
|
||||
}
|
||||
|
||||
static sf_count_t isf_seek(sf_count_t offset, int whence, void *user_data)
|
||||
{
|
||||
Stream *fw = (Stream*)user_data;
|
||||
|
||||
try
|
||||
{
|
||||
//printf("Seek: offset=%lld, whence=%lld\n", (long long)offset, (long long)whence);
|
||||
|
||||
fw->seek(offset, whence);
|
||||
return fw->tell();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
//printf(" SEEK FAILED\n");
|
||||
return(-1);
|
||||
}
|
||||
}
|
||||
|
||||
static sf_count_t isf_read(void *ptr, sf_count_t count, void *user_data)
|
||||
{
|
||||
Stream *fw = (Stream*)user_data;
|
||||
|
||||
try
|
||||
{
|
||||
sf_count_t ret = fw->read(ptr, count, false);
|
||||
|
||||
//printf("Read: count=%lld, ret=%lld\n", (long long)count, (long long)ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
//printf(" READ FAILED\n");
|
||||
return(0);
|
||||
}
|
||||
}
|
||||
|
||||
static sf_count_t isf_write(const void *ptr, sf_count_t count, void *user_data)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
static sf_count_t isf_tell(void *user_data)
|
||||
{
|
||||
Stream *fw = (Stream*)user_data;
|
||||
|
||||
try
|
||||
{
|
||||
return fw->tell();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return(-1);
|
||||
}
|
||||
}
|
||||
|
||||
CDAFReader_SF::CDAFReader_SF(Stream *fp) : fw(fp)
|
||||
{
|
||||
memset(&sfvf, 0, sizeof(sfvf));
|
||||
sfvf.get_filelen = isf_get_filelen;
|
||||
sfvf.seek = isf_seek;
|
||||
sfvf.read = isf_read;
|
||||
sfvf.write = isf_write;
|
||||
sfvf.tell = isf_tell;
|
||||
|
||||
memset(&sfinfo, 0, sizeof(sfinfo));
|
||||
if(!(sf = sf_open_virtual(&sfvf, SFM_READ, &sfinfo, (void*)fp)))
|
||||
throw(0);
|
||||
}
|
||||
|
||||
CDAFReader_SF::~CDAFReader_SF()
|
||||
{
|
||||
sf_close(sf);
|
||||
}
|
||||
|
||||
uint64 CDAFReader_SF::Read_(int16 *buffer, uint64 frames)
|
||||
{
|
||||
return(sf_read_short(sf, (short*)buffer, frames * 2) / 2);
|
||||
}
|
||||
|
||||
bool CDAFReader_SF::Seek_(uint64 frame_offset)
|
||||
{
|
||||
// FIXME error condition
|
||||
if((uint64)sf_seek(sf, frame_offset, SEEK_SET) != frame_offset)
|
||||
return(false);
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
uint64 CDAFReader_SF::FrameCount(void)
|
||||
{
|
||||
return(sfinfo.frames);
|
||||
}
|
||||
|
||||
|
||||
CDAFReader* CDAFR_SF_Open(Stream* fp)
|
||||
{
|
||||
return new CDAFReader_SF(fp);
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
#ifndef __MDFN_CDAFREADER_SF_H
|
||||
#define __MDFN_CDAFREADER_SF_H
|
||||
|
||||
CDAFReader* CDAFR_SF_Open(Stream* fp);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,158 @@
|
|||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <mednafen/mednafen.h>
|
||||
#include "CDAFReader.h"
|
||||
#include "CDAFReader_Vorbis.h"
|
||||
|
||||
#if 0
|
||||
#include <tremor/ivorbisfile.h>
|
||||
#else
|
||||
#include <mednafen/tremor/ivorbisfile.h>
|
||||
#endif
|
||||
|
||||
class CDAFReader_Vorbis final : public CDAFReader
|
||||
{
|
||||
public:
|
||||
CDAFReader_Vorbis(Stream *fp);
|
||||
~CDAFReader_Vorbis();
|
||||
|
||||
uint64 Read_(int16 *buffer, uint64 frames) override;
|
||||
bool Seek_(uint64 frame_offset) override;
|
||||
uint64 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);
|
||||
|
||||
try
|
||||
{
|
||||
return fw->read(ptr, size * nmemb, false) / size;
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
}
|
||||
|
||||
static int iov_seek_func(void *user_data, ogg_int64_t offset, int whence)
|
||||
{
|
||||
Stream *fw = (Stream*)user_data;
|
||||
|
||||
try
|
||||
{
|
||||
fw->seek(offset, whence);
|
||||
return(0);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return(-1);
|
||||
}
|
||||
}
|
||||
|
||||
static int iov_close_func(void *user_data)
|
||||
{
|
||||
Stream *fw = (Stream*)user_data;
|
||||
|
||||
try
|
||||
{
|
||||
fw->close();
|
||||
return(0);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return EOF;
|
||||
}
|
||||
}
|
||||
|
||||
static long iov_tell_func(void *user_data)
|
||||
{
|
||||
Stream *fw = (Stream*)user_data;
|
||||
|
||||
try
|
||||
{
|
||||
return fw->tell();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return(-1);
|
||||
}
|
||||
}
|
||||
|
||||
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 CDAFReader_Vorbis::Read_(int16 *buffer, uint64 frames)
|
||||
{
|
||||
uint8 *tw_buf = (uint8 *)buffer;
|
||||
int cursection = 0;
|
||||
long toread = frames * sizeof(int16) * 2;
|
||||
|
||||
while(toread > 0)
|
||||
{
|
||||
long didread = ov_read(&ovfile, (char*)tw_buf, toread, &cursection);
|
||||
|
||||
if(didread == 0)
|
||||
break;
|
||||
|
||||
tw_buf = (uint8 *)tw_buf + didread;
|
||||
toread -= didread;
|
||||
}
|
||||
|
||||
return(frames - toread / sizeof(int16) / 2);
|
||||
}
|
||||
|
||||
bool CDAFReader_Vorbis::Seek_(uint64 frame_offset)
|
||||
{
|
||||
ov_pcm_seek(&ovfile, frame_offset);
|
||||
return(true);
|
||||
}
|
||||
|
||||
uint64 CDAFReader_Vorbis::FrameCount(void)
|
||||
{
|
||||
return(ov_pcm_total(&ovfile, -1));
|
||||
}
|
||||
|
||||
CDAFReader* CDAFR_Vorbis_Open(Stream* fp)
|
||||
{
|
||||
return new CDAFReader_Vorbis(fp);
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
#ifndef __MDFN_CDAFREADER_VORBIS_H
|
||||
#define __MDFN_CDAFREADER_VORBIS_H
|
||||
|
||||
CDAFReader* CDAFR_Vorbis_Open(Stream* fp);
|
||||
|
||||
#endif
|
||||
|
|
@ -1,58 +1,46 @@
|
|||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "emuware/emuware.h"
|
||||
#include "CDAccess.h"
|
||||
#include "CDAccess_Image.h"
|
||||
#include "CDAccess_CCD.h"
|
||||
|
||||
#ifdef HAVE_LIBCDIO
|
||||
#include "CDAccess_Physical.h"
|
||||
#endif
|
||||
|
||||
using namespace CDUtility;
|
||||
|
||||
CDAccess::CDAccess()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CDAccess::~CDAccess()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CDAccess *cdaccess_open_image(const std::string& path, bool image_memcache)
|
||||
{
|
||||
CDAccess *ret = NULL;
|
||||
|
||||
if(path.size() >= 4 && !strcasecmp(path.c_str() + path.size() - 4, ".ccd"))
|
||||
ret = new CDAccess_CCD(path, image_memcache);
|
||||
else
|
||||
ret = new CDAccess_Image(path, image_memcache);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
CDAccess *cdaccess_open_phys(const std::string& devicename)
|
||||
{
|
||||
#ifdef HAVE_LIBCDIO
|
||||
return new CDAccess_Physical(devicename);
|
||||
#else
|
||||
throw MDFN_Error(0, ("Physical CD access support not compiled in."));
|
||||
#endif
|
||||
}
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "emuware/emuware.h"
|
||||
#include "CDAccess.h"
|
||||
#include "CDAccess_Image.h"
|
||||
#include "CDAccess_CCD.h"
|
||||
|
||||
using namespace CDUtility;
|
||||
|
||||
CDAccess::CDAccess()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CDAccess::~CDAccess()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CDAccess* CDAccess_Open(const std::string& path, bool image_memcache)
|
||||
{
|
||||
CDAccess *ret = NULL;
|
||||
|
||||
if(path.size() >= 4 && !strcasecmp(path.c_str() + path.size() - 4, ".ccd"))
|
||||
ret = new CDAccess_CCD(path, image_memcache);
|
||||
else
|
||||
ret = new CDAccess_Image(path, image_memcache);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,32 +1,33 @@
|
|||
#ifndef __MDFN_CDROMFILE_H
|
||||
#define __MDFN_CDROMFILE_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
|
||||
#include "CDUtility.h"
|
||||
|
||||
class CDAccess
|
||||
{
|
||||
public:
|
||||
|
||||
CDAccess();
|
||||
virtual ~CDAccess();
|
||||
|
||||
virtual void Read_Raw_Sector(uint8 *buf, int32 lba) = 0;
|
||||
|
||||
virtual void Read_TOC(CDUtility::TOC *toc) = 0;
|
||||
|
||||
virtual bool Is_Physical(void) throw() = 0;
|
||||
|
||||
virtual void Eject(bool eject_status) = 0; // Eject a disc if it's physical, otherwise NOP. Returns true on success(or NOP), false on error
|
||||
|
||||
private:
|
||||
CDAccess(const CDAccess&); // No copy constructor.
|
||||
CDAccess& operator=(const CDAccess&); // No assignment operator.
|
||||
};
|
||||
|
||||
CDAccess *cdaccess_open_image(const std::string& path, bool image_memcache);
|
||||
CDAccess *cdaccess_open_phys(const std::string& devicename);
|
||||
|
||||
#endif
|
||||
#ifndef __MDFN_CDROMFILE_H
|
||||
#define __MDFN_CDROMFILE_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
|
||||
#include "CDUtility.h"
|
||||
|
||||
class CDAccess
|
||||
{
|
||||
public:
|
||||
|
||||
CDAccess();
|
||||
virtual ~CDAccess();
|
||||
|
||||
virtual void Read_Raw_Sector(uint8 *buf, int32 lba) = 0;
|
||||
|
||||
// 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* pwbuf, int32 lba) const noexcept = 0;
|
||||
|
||||
virtual void Read_TOC(CDUtility::TOC *toc) = 0;
|
||||
|
||||
private:
|
||||
CDAccess(const CDAccess&); // No copy constructor.
|
||||
CDAccess& operator=(const CDAccess&); // No assignment operator.
|
||||
};
|
||||
|
||||
CDAccess* CDAccess_Open(const std::string& path, bool image_memcache);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,435 +1,432 @@
|
|||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "emuware/emuware.h"
|
||||
#include "../general.h"
|
||||
#include "../string/trim.h"
|
||||
#include "CDAccess_CCD.h"
|
||||
//#include <trio/trio.h>
|
||||
|
||||
//wrapper to repair gettext stuff
|
||||
#define _(X) X
|
||||
|
||||
#include <limits>
|
||||
#include <limits.h>
|
||||
#include <map>
|
||||
|
||||
using namespace CDUtility;
|
||||
|
||||
static void MDFN_strtoupper(std::string &str)
|
||||
{
|
||||
const size_t len = str.length();
|
||||
|
||||
for(size_t x = 0; x < len; x++)
|
||||
{
|
||||
if(str[x] >= 'a' && str[x] <= 'z')
|
||||
{
|
||||
str[x] = str[x] - 'a' + 'A';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typedef std::map<std::string, std::string> CCD_Section;
|
||||
|
||||
template<typename T>
|
||||
static T CCD_ReadInt(CCD_Section &s, const std::string &propname, const bool have_defval = false, const int defval = 0)
|
||||
{
|
||||
CCD_Section::iterator zit = s.find(propname);
|
||||
|
||||
if(zit == s.end())
|
||||
{
|
||||
if(have_defval)
|
||||
return defval;
|
||||
else
|
||||
throw MDFN_Error(0, _("Missing property: %s"), propname.c_str());
|
||||
}
|
||||
|
||||
const std::string &v = zit->second;
|
||||
int scan_base = 10;
|
||||
size_t scan_offset = 0;
|
||||
long ret = 0;
|
||||
|
||||
if(v.length() >= 3 && v[0] == '0' && v[1] == 'x')
|
||||
{
|
||||
scan_base = 16;
|
||||
scan_offset = 2;
|
||||
}
|
||||
|
||||
const char *vp = v.c_str() + scan_offset;
|
||||
char *ep = NULL;
|
||||
|
||||
if(std::numeric_limits<T>::is_signed)
|
||||
ret = strtol(vp, &ep, scan_base);
|
||||
else
|
||||
ret = strtoul(vp, &ep, scan_base);
|
||||
|
||||
if(!vp[0] || ep[0])
|
||||
{
|
||||
throw MDFN_Error(0, _("Property %s: Malformed integer: %s"), propname.c_str(), v.c_str());
|
||||
}
|
||||
|
||||
//if(ret < minv || ret > maxv)
|
||||
//{
|
||||
// throw MDFN_Error(0, _("Property %s: Integer %ld out of range(accepted: %d through %d)."), propname.c_str(), ret, minv, maxv);
|
||||
//}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
CDAccess_CCD::CDAccess_CCD(const std::string& path, bool image_memcache) : img_stream(NULL), sub_stream(NULL), img_numsectors(0)
|
||||
{
|
||||
try
|
||||
{
|
||||
Load(path, image_memcache);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
Cleanup();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void CDAccess_CCD::Load(const std::string& path, bool image_memcache)
|
||||
{
|
||||
FileStream cf(path, FileStream::MODE_READ);
|
||||
std::map<std::string, CCD_Section> Sections;
|
||||
std::string linebuf;
|
||||
std::string cur_section_name;
|
||||
std::string dir_path, file_base, file_ext;
|
||||
char img_extsd[4] = { 'i', 'm', 'g', 0 };
|
||||
char sub_extsd[4] = { 's', 'u', 'b', 0 };
|
||||
|
||||
MDFN_GetFilePathComponents(path, &dir_path, &file_base, &file_ext);
|
||||
|
||||
if(file_ext.length() == 4 && file_ext[0] == '.')
|
||||
{
|
||||
signed char extupt[3] = { -1, -1, -1 };
|
||||
|
||||
for(int i = 1; i < 4; i++)
|
||||
{
|
||||
if(file_ext[i] >= 'A' && file_ext[i] <= 'Z')
|
||||
extupt[i - 1] = 'A' - 'a';
|
||||
else if(file_ext[i] >= 'a' && file_ext[i] <= 'z')
|
||||
extupt[i - 1] = 0;
|
||||
}
|
||||
|
||||
signed char av = -1;
|
||||
for(int i = 0; i < 3; i++)
|
||||
{
|
||||
if(extupt[i] != -1)
|
||||
av = extupt[i];
|
||||
else
|
||||
extupt[i] = av;
|
||||
}
|
||||
|
||||
if(av == -1)
|
||||
av = 0;
|
||||
|
||||
for(int i = 0; i < 3; i++)
|
||||
{
|
||||
if(extupt[i] == -1)
|
||||
extupt[i] = av;
|
||||
}
|
||||
|
||||
for(int i = 0; i < 3; i++)
|
||||
{
|
||||
img_extsd[i] += extupt[i];
|
||||
sub_extsd[i] += extupt[i];
|
||||
}
|
||||
}
|
||||
|
||||
//printf("%s %d %d %d\n", file_ext.c_str(), extupt[0], extupt[1], extupt[2]);
|
||||
|
||||
linebuf.reserve(256);
|
||||
|
||||
while(cf.get_line(linebuf) >= 0)
|
||||
{
|
||||
MDFN_trim(linebuf);
|
||||
|
||||
if(linebuf.length() == 0) // Skip blank lines.
|
||||
continue;
|
||||
|
||||
if(linebuf[0] == '[')
|
||||
{
|
||||
if(linebuf.length() < 3 || linebuf[linebuf.length() - 1] != ']')
|
||||
throw MDFN_Error(0, _("Malformed section specifier: %s"), linebuf.c_str());
|
||||
|
||||
cur_section_name = linebuf.substr(1, linebuf.length() - 2);
|
||||
MDFN_strtoupper(cur_section_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
const size_t feqpos = linebuf.find('=');
|
||||
const size_t leqpos = linebuf.rfind('=');
|
||||
std::string k, v;
|
||||
|
||||
if(feqpos == std::string::npos || feqpos != leqpos)
|
||||
throw MDFN_Error(0, _("Malformed value pair specifier: %s"), linebuf.c_str());
|
||||
|
||||
k = linebuf.substr(0, feqpos);
|
||||
v = linebuf.substr(feqpos + 1);
|
||||
|
||||
MDFN_trim(k);
|
||||
MDFN_trim(v);
|
||||
|
||||
MDFN_strtoupper(k);
|
||||
|
||||
Sections[cur_section_name][k] = v;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
CCD_Section& ds = Sections["DISC"];
|
||||
unsigned toc_entries = CCD_ReadInt<unsigned>(ds, "TOCENTRIES");
|
||||
unsigned num_sessions = CCD_ReadInt<unsigned>(ds, "SESSIONS");
|
||||
bool data_tracks_scrambled = CCD_ReadInt<unsigned>(ds, "DATATRACKSSCRAMBLED");
|
||||
|
||||
if(num_sessions != 1)
|
||||
throw MDFN_Error(0, _("Unsupported number of sessions: %u"), num_sessions);
|
||||
|
||||
if(data_tracks_scrambled)
|
||||
throw MDFN_Error(0, _("Scrambled CCD data tracks currently not supported."));
|
||||
|
||||
//printf("MOO: %d\n", toc_entries);
|
||||
|
||||
for(unsigned te = 0; te < toc_entries; te++)
|
||||
{
|
||||
char tmpbuf[64];
|
||||
snprintf(tmpbuf, sizeof(tmpbuf), "ENTRY %u", te);
|
||||
CCD_Section& ts = Sections[std::string(tmpbuf)];
|
||||
unsigned session = CCD_ReadInt<unsigned>(ts, "SESSION");
|
||||
uint8 point = CCD_ReadInt<uint8>(ts, "POINT");
|
||||
uint8 adr = CCD_ReadInt<uint8>(ts, "ADR");
|
||||
uint8 control = CCD_ReadInt<uint8>(ts, "CONTROL");
|
||||
uint8 pmin = CCD_ReadInt<uint8>(ts, "PMIN");
|
||||
uint8 psec = CCD_ReadInt<uint8>(ts, "PSEC");
|
||||
//uint8 pframe = CCD_ReadInt<uint8>(ts, "PFRAME");
|
||||
signed plba = CCD_ReadInt<signed>(ts, "PLBA");
|
||||
|
||||
if(session != 1)
|
||||
throw MDFN_Error(0, "Unsupported TOC entry Session value: %u", session);
|
||||
|
||||
// Reference: ECMA-394, page 5-14
|
||||
if(point >= 1 && point <= 99)
|
||||
{
|
||||
tocd.tracks[point].adr = adr;
|
||||
tocd.tracks[point].control = control;
|
||||
tocd.tracks[point].lba = plba;
|
||||
}
|
||||
else
|
||||
switch(point)
|
||||
{
|
||||
default:
|
||||
throw MDFN_Error(0, "Unsupported TOC entry Point value: %u", point);
|
||||
break;
|
||||
|
||||
case 0xA0:
|
||||
tocd.first_track = pmin;
|
||||
tocd.disc_type = psec;
|
||||
break;
|
||||
|
||||
case 0xA1:
|
||||
tocd.last_track = pmin;
|
||||
break;
|
||||
|
||||
case 0xA2:
|
||||
tocd.tracks[100].adr = adr;
|
||||
tocd.tracks[100].control = control;
|
||||
tocd.tracks[100].lba = plba;
|
||||
break;
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Convenience leadout track duplication.
|
||||
if(tocd.last_track < 99)
|
||||
tocd.tracks[tocd.last_track + 1] = tocd.tracks[100];
|
||||
|
||||
//
|
||||
// Open image stream.
|
||||
{
|
||||
std::string image_path = MDFN_EvalFIP(dir_path, file_base + std::string(".") + std::string(img_extsd), true);
|
||||
|
||||
if(image_memcache)
|
||||
{
|
||||
img_stream = new MemoryStream(new FileStream(image_path, FileStream::MODE_READ));
|
||||
}
|
||||
else
|
||||
{
|
||||
img_stream = new FileStream(image_path, FileStream::MODE_READ);
|
||||
}
|
||||
|
||||
int64 ss = img_stream->size();
|
||||
|
||||
if(ss % 2352)
|
||||
throw MDFN_Error(0, _("CCD image size is not evenly divisible by 2352."));
|
||||
|
||||
img_numsectors = ss / 2352;
|
||||
}
|
||||
|
||||
//
|
||||
// Open subchannel stream
|
||||
{
|
||||
std::string sub_path = MDFN_EvalFIP(dir_path, file_base + std::string(".") + std::string(sub_extsd), true);
|
||||
|
||||
if(image_memcache)
|
||||
sub_stream = new MemoryStream(new FileStream(sub_path, FileStream::MODE_READ));
|
||||
else
|
||||
sub_stream = new FileStream(sub_path, FileStream::MODE_READ);
|
||||
|
||||
if(sub_stream->size() != (uint64)img_numsectors * 96)
|
||||
throw MDFN_Error(0, _("CCD SUB file size mismatch."));
|
||||
}
|
||||
|
||||
CheckSubQSanity();
|
||||
}
|
||||
|
||||
//
|
||||
// Checks for Q subchannel mode 1(current time) data that has a correct checksum, but the data is nonsensical or corrupted nonetheless; this is the
|
||||
// case for some bad rips floating around on the Internet. Allowing these bad rips to be used will cause all sorts of problems during emulation, so we
|
||||
// error out here if a bad rip is detected.
|
||||
//
|
||||
// This check is not as aggressive or exhaustive as it could be, and will not detect all potential Q subchannel rip errors; as such, it should definitely NOT be
|
||||
// used in an effort to "repair" a broken rip.
|
||||
//
|
||||
void CDAccess_CCD::CheckSubQSanity(void)
|
||||
{
|
||||
size_t checksum_pass_counter = 0;
|
||||
int prev_lba = INT_MAX;
|
||||
uint8 prev_track = 0;
|
||||
|
||||
for(size_t s = 0; s < img_numsectors; s++)
|
||||
{
|
||||
union
|
||||
{
|
||||
uint8 full[96];
|
||||
struct
|
||||
{
|
||||
uint8 pbuf[12];
|
||||
uint8 qbuf[12];
|
||||
};
|
||||
} buf;
|
||||
|
||||
sub_stream->seek(s * 96, SEEK_SET);
|
||||
sub_stream->read(buf.full, 96);
|
||||
|
||||
if(subq_check_checksum(buf.qbuf))
|
||||
{
|
||||
uint8 adr = buf.qbuf[0] & 0xF;
|
||||
|
||||
if(adr == 0x01)
|
||||
{
|
||||
uint8 track_bcd = buf.qbuf[1];
|
||||
uint8 index_bcd = buf.qbuf[2];
|
||||
uint8 rm_bcd = buf.qbuf[3];
|
||||
uint8 rs_bcd = buf.qbuf[4];
|
||||
uint8 rf_bcd = buf.qbuf[5];
|
||||
uint8 am_bcd = buf.qbuf[7];
|
||||
uint8 as_bcd = buf.qbuf[8];
|
||||
uint8 af_bcd = buf.qbuf[9];
|
||||
|
||||
//printf("%2x %2x %2x\n", am_bcd, as_bcd, af_bcd);
|
||||
|
||||
if(!BCD_is_valid(track_bcd) || !BCD_is_valid(index_bcd) || !BCD_is_valid(rm_bcd) || !BCD_is_valid(rs_bcd) || !BCD_is_valid(rf_bcd) ||
|
||||
!BCD_is_valid(am_bcd) || !BCD_is_valid(as_bcd) || !BCD_is_valid(af_bcd) ||
|
||||
rs_bcd > 0x59 || rf_bcd > 0x74 || as_bcd > 0x59 || af_bcd > 0x74)
|
||||
{
|
||||
throw MDFN_Error(0, _("Garbage subchannel Q data detected(bad BCD/out of range): %02x:%02x:%02x %02x:%02x:%02x"), rm_bcd, rs_bcd, rf_bcd, am_bcd, as_bcd, af_bcd);
|
||||
}
|
||||
else
|
||||
{
|
||||
int lba = ((BCD_to_U8(am_bcd) * 60 + BCD_to_U8(as_bcd)) * 75 + BCD_to_U8(af_bcd)) - 150;
|
||||
uint8 track = BCD_to_U8(track_bcd);
|
||||
|
||||
if(prev_lba != INT_MAX && abs(lba - prev_lba) > 100)
|
||||
throw MDFN_Error(0, _("Garbage subchannel Q data detected(excessively large jump in AMSF)"));
|
||||
|
||||
if(abs(lba - (int)s) > 100)
|
||||
throw MDFN_Error(0, _("Garbage subchannel Q data detected(AMSF value is out of tolerance)"));
|
||||
|
||||
prev_lba = lba;
|
||||
|
||||
if(track < prev_track)
|
||||
throw MDFN_Error(0, _("Garbage subchannel Q data detected(bad track number)"));
|
||||
//else if(prev_track && track - pre
|
||||
|
||||
prev_track = track;
|
||||
}
|
||||
checksum_pass_counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//printf("%u/%u\n", checksum_pass_counter, img_numsectors);
|
||||
}
|
||||
|
||||
void CDAccess_CCD::Cleanup(void)
|
||||
{
|
||||
if(img_stream)
|
||||
{
|
||||
delete img_stream;
|
||||
img_stream = NULL;
|
||||
}
|
||||
|
||||
if(sub_stream)
|
||||
{
|
||||
delete sub_stream;
|
||||
sub_stream = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
CDAccess_CCD::~CDAccess_CCD()
|
||||
{
|
||||
Cleanup();
|
||||
}
|
||||
|
||||
void CDAccess_CCD::Read_Raw_Sector(uint8 *buf, int32 lba)
|
||||
{
|
||||
if(lba < 0 || (size_t)lba >= img_numsectors)
|
||||
throw(MDFN_Error(0, _("LBA out of range.")));
|
||||
|
||||
uint8 sub_buf[96];
|
||||
|
||||
img_stream->seek(lba * 2352, SEEK_SET);
|
||||
img_stream->read(buf, 2352);
|
||||
|
||||
sub_stream->seek(lba * 96, SEEK_SET);
|
||||
sub_stream->read(sub_buf, 96);
|
||||
|
||||
subpw_interleave(sub_buf, buf + 2352);
|
||||
}
|
||||
|
||||
|
||||
void CDAccess_CCD::Read_TOC(CDUtility::TOC *toc)
|
||||
{
|
||||
*toc = tocd;
|
||||
}
|
||||
|
||||
bool CDAccess_CCD::Is_Physical(void) throw()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void CDAccess_CCD::Eject(bool eject_status)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
//#define CHECK_CCD_GARBAGE_CUBQ_A
|
||||
//#define CHECK_CCD_GARBAGE_CUBQ_B
|
||||
//#define CHECK_CCD_GARBAGE_CUBQ_C
|
||||
//#define CHECK_CCD_GARBAGE_CUBQ_D
|
||||
|
||||
#include "emuware/emuware.h"
|
||||
#include "../general.h"
|
||||
#include "../string/trim.h"
|
||||
#include "CDAccess_CCD.h"
|
||||
#include <trio/trio.h>
|
||||
|
||||
//wrapper to repair gettext stuff
|
||||
#define _(X) X
|
||||
|
||||
#include <limits>
|
||||
#include <limits.h>
|
||||
#include <map>
|
||||
|
||||
using namespace CDUtility;
|
||||
|
||||
static void MDFN_strtoupper(std::string &str)
|
||||
{
|
||||
const size_t len = str.length();
|
||||
|
||||
for(size_t x = 0; x < len; x++)
|
||||
{
|
||||
if(str[x] >= 'a' && str[x] <= 'z')
|
||||
{
|
||||
str[x] = str[x] - 'a' + 'A';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typedef std::map<std::string, std::string> CCD_Section;
|
||||
|
||||
template<typename T>
|
||||
static T CCD_ReadInt(CCD_Section &s, const std::string &propname, const bool have_defval = false, const int defval = 0)
|
||||
{
|
||||
CCD_Section::iterator zit = s.find(propname);
|
||||
|
||||
if(zit == s.end())
|
||||
{
|
||||
if(have_defval)
|
||||
return defval;
|
||||
else
|
||||
throw MDFN_Error(0, _("Missing property: %s"), propname.c_str());
|
||||
}
|
||||
|
||||
const std::string &v = zit->second;
|
||||
int scan_base = 10;
|
||||
size_t scan_offset = 0;
|
||||
long ret = 0;
|
||||
|
||||
if(v.length() >= 3 && v[0] == '0' && v[1] == 'x')
|
||||
{
|
||||
scan_base = 16;
|
||||
scan_offset = 2;
|
||||
}
|
||||
|
||||
const char *vp = v.c_str() + scan_offset;
|
||||
char *ep = NULL;
|
||||
|
||||
if(std::numeric_limits<T>::is_signed)
|
||||
ret = strtol(vp, &ep, scan_base);
|
||||
else
|
||||
ret = strtoul(vp, &ep, scan_base);
|
||||
|
||||
if(!vp[0] || ep[0])
|
||||
{
|
||||
throw MDFN_Error(0, _("Property %s: Malformed integer: %s"), propname.c_str(), v.c_str());
|
||||
}
|
||||
|
||||
//if(ret < minv || ret > maxv)
|
||||
//{
|
||||
// throw MDFN_Error(0, _("Property %s: Integer %ld out of range(accepted: %d through %d)."), propname.c_str(), ret, minv, maxv);
|
||||
//}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
CDAccess_CCD::CDAccess_CCD(const std::string& path, bool image_memcache) : img_numsectors(0)
|
||||
{
|
||||
Load(path, image_memcache);
|
||||
}
|
||||
|
||||
void CDAccess_CCD::Load(const std::string& path, bool image_memcache)
|
||||
{
|
||||
FileStream cf(path, FileStream::MODE_READ);
|
||||
std::map<std::string, CCD_Section> Sections;
|
||||
std::string linebuf;
|
||||
std::string cur_section_name;
|
||||
std::string dir_path, file_base, file_ext;
|
||||
char img_extsd[4] = { 'i', 'm', 'g', 0 };
|
||||
char sub_extsd[4] = { 's', 'u', 'b', 0 };
|
||||
|
||||
MDFN_GetFilePathComponents(path, &dir_path, &file_base, &file_ext);
|
||||
|
||||
if(file_ext.length() == 4 && file_ext[0] == '.')
|
||||
{
|
||||
signed char extupt[3] = { -1, -1, -1 };
|
||||
|
||||
for(int i = 1; i < 4; i++)
|
||||
{
|
||||
if(file_ext[i] >= 'A' && file_ext[i] <= 'Z')
|
||||
extupt[i - 1] = 'A' - 'a';
|
||||
else if(file_ext[i] >= 'a' && file_ext[i] <= 'z')
|
||||
extupt[i - 1] = 0;
|
||||
}
|
||||
|
||||
signed char av = -1;
|
||||
for(int i = 0; i < 3; i++)
|
||||
{
|
||||
if(extupt[i] != -1)
|
||||
av = extupt[i];
|
||||
else
|
||||
extupt[i] = av;
|
||||
}
|
||||
|
||||
if(av == -1)
|
||||
av = 0;
|
||||
|
||||
for(int i = 0; i < 3; i++)
|
||||
{
|
||||
if(extupt[i] == -1)
|
||||
extupt[i] = av;
|
||||
}
|
||||
|
||||
for(int i = 0; i < 3; i++)
|
||||
{
|
||||
img_extsd[i] += extupt[i];
|
||||
sub_extsd[i] += extupt[i];
|
||||
}
|
||||
}
|
||||
|
||||
//printf("%s %d %d %d\n", file_ext.c_str(), extupt[0], extupt[1], extupt[2]);
|
||||
|
||||
linebuf.reserve(256);
|
||||
|
||||
while(cf.get_line(linebuf) >= 0)
|
||||
{
|
||||
MDFN_trim(linebuf);
|
||||
|
||||
if(linebuf.length() == 0) // Skip blank lines.
|
||||
continue;
|
||||
|
||||
if(linebuf[0] == '[')
|
||||
{
|
||||
if(linebuf.length() < 3 || linebuf[linebuf.length() - 1] != ']')
|
||||
throw MDFN_Error(0, _("Malformed section specifier: %s"), linebuf.c_str());
|
||||
|
||||
cur_section_name = linebuf.substr(1, linebuf.length() - 2);
|
||||
MDFN_strtoupper(cur_section_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
const size_t feqpos = linebuf.find('=');
|
||||
const size_t leqpos = linebuf.rfind('=');
|
||||
std::string k, v;
|
||||
|
||||
if(feqpos == std::string::npos || feqpos != leqpos)
|
||||
throw MDFN_Error(0, _("Malformed value pair specifier: %s"), linebuf.c_str());
|
||||
|
||||
k = linebuf.substr(0, feqpos);
|
||||
v = linebuf.substr(feqpos + 1);
|
||||
|
||||
MDFN_trim(k);
|
||||
MDFN_trim(v);
|
||||
|
||||
MDFN_strtoupper(k);
|
||||
|
||||
Sections[cur_section_name][k] = v;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
CCD_Section& ds = Sections["DISC"];
|
||||
unsigned toc_entries = CCD_ReadInt<unsigned>(ds, "TOCENTRIES");
|
||||
unsigned num_sessions = CCD_ReadInt<unsigned>(ds, "SESSIONS");
|
||||
bool data_tracks_scrambled = CCD_ReadInt<unsigned>(ds, "DATATRACKSSCRAMBLED");
|
||||
|
||||
if(num_sessions != 1)
|
||||
throw MDFN_Error(0, _("Unsupported number of sessions: %u"), num_sessions);
|
||||
|
||||
if(data_tracks_scrambled)
|
||||
throw MDFN_Error(0, _("Scrambled CCD data tracks currently not supported."));
|
||||
|
||||
//printf("MOO: %d\n", toc_entries);
|
||||
|
||||
for(unsigned te = 0; te < toc_entries; te++)
|
||||
{
|
||||
char tmpbuf[64];
|
||||
trio_snprintf(tmpbuf, sizeof(tmpbuf), "ENTRY %u", te);
|
||||
CCD_Section& ts = Sections[std::string(tmpbuf)];
|
||||
unsigned session = CCD_ReadInt<unsigned>(ts, "SESSION");
|
||||
uint8 point = CCD_ReadInt<uint8>(ts, "POINT");
|
||||
uint8 adr = CCD_ReadInt<uint8>(ts, "ADR");
|
||||
uint8 control = CCD_ReadInt<uint8>(ts, "CONTROL");
|
||||
uint8 pmin = CCD_ReadInt<uint8>(ts, "PMIN");
|
||||
uint8 psec = CCD_ReadInt<uint8>(ts, "PSEC");
|
||||
//uint8 pframe = CCD_ReadInt<uint8>(ts, "PFRAME");
|
||||
signed plba = CCD_ReadInt<signed>(ts, "PLBA");
|
||||
|
||||
if(session != 1)
|
||||
throw MDFN_Error(0, "Unsupported TOC entry Session value: %u", session);
|
||||
|
||||
// Reference: ECMA-394, page 5-14
|
||||
if(point >= 1 && point <= 99)
|
||||
{
|
||||
tocd.tracks[point].adr = adr;
|
||||
tocd.tracks[point].control = control;
|
||||
tocd.tracks[point].lba = plba;
|
||||
tocd.tracks[point].valid = true;
|
||||
}
|
||||
else switch(point)
|
||||
{
|
||||
default:
|
||||
throw MDFN_Error(0, "Unsupported TOC entry Point value: %u", point);
|
||||
break;
|
||||
|
||||
case 0xA0:
|
||||
tocd.first_track = pmin;
|
||||
tocd.disc_type = psec;
|
||||
break;
|
||||
|
||||
case 0xA1:
|
||||
tocd.last_track = pmin;
|
||||
break;
|
||||
|
||||
case 0xA2:
|
||||
tocd.tracks[100].adr = adr;
|
||||
tocd.tracks[100].control = control;
|
||||
tocd.tracks[100].lba = plba;
|
||||
tocd.tracks[100].valid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Open image stream.
|
||||
{
|
||||
std::string image_path = MDFN_EvalFIP(dir_path, file_base + std::string(".") + std::string(img_extsd), true);
|
||||
|
||||
if(image_memcache)
|
||||
{
|
||||
img_stream.reset(new MemoryStream(new FileStream(image_path, FileStream::MODE_READ)));
|
||||
}
|
||||
else
|
||||
{
|
||||
img_stream.reset(new FileStream(image_path, FileStream::MODE_READ));
|
||||
}
|
||||
|
||||
uint64 ss = img_stream->size();
|
||||
|
||||
if(ss % 2352)
|
||||
throw MDFN_Error(0, _("CCD image size is not evenly divisible by 2352."));
|
||||
|
||||
if(ss > 0x7FFFFFFF)
|
||||
throw MDFN_Error(0, _("CCD image is too large."));
|
||||
|
||||
img_numsectors = ss / 2352;
|
||||
}
|
||||
|
||||
//
|
||||
// Open subchannel stream
|
||||
{
|
||||
std::string sub_path = MDFN_EvalFIP(dir_path, file_base + std::string(".") + std::string(sub_extsd), true);
|
||||
FileStream sub_stream(sub_path, FileStream::MODE_READ);
|
||||
|
||||
if(sub_stream.size() != (uint64)img_numsectors * 96)
|
||||
throw MDFN_Error(0, _("CCD SUB file size mismatch."));
|
||||
|
||||
sub_data.reset(new uint8[(uint64)img_numsectors * 96]);
|
||||
sub_stream.read(sub_data.get(), (uint64)img_numsectors * 96);
|
||||
}
|
||||
|
||||
CheckSubQSanity();
|
||||
}
|
||||
|
||||
//
|
||||
// Checks for Q subchannel mode 1(current time) data that has a correct checksum, but the data is nonsensical or corrupted nonetheless; this is the
|
||||
// case for some bad rips floating around on the Internet. Allowing these bad rips to be used will cause all sorts of problems during emulation, so we
|
||||
// error out here if a bad rip is detected.
|
||||
//
|
||||
// This check is not as aggressive or exhaustive as it could be, and will not detect all potential Q subchannel rip errors; as such, it should definitely NOT be
|
||||
// used in an effort to "repair" a broken rip.
|
||||
//
|
||||
void CDAccess_CCD::CheckSubQSanity(void)
|
||||
{
|
||||
size_t checksum_pass_counter = 0;
|
||||
int prev_lba = INT_MAX;
|
||||
uint8 prev_track = 0;
|
||||
|
||||
for(size_t s = 0; s < img_numsectors; s++)
|
||||
{
|
||||
union
|
||||
{
|
||||
uint8 full[96];
|
||||
struct
|
||||
{
|
||||
uint8 pbuf[12];
|
||||
uint8 qbuf[12];
|
||||
};
|
||||
} buf;
|
||||
|
||||
memcpy(buf.full, &sub_data[s * 96], 96);
|
||||
|
||||
if(subq_check_checksum(buf.qbuf))
|
||||
{
|
||||
uint8 adr = buf.qbuf[0] & 0xF;
|
||||
|
||||
if(adr == 0x01)
|
||||
{
|
||||
uint8 track_bcd = buf.qbuf[1];
|
||||
uint8 index_bcd = buf.qbuf[2];
|
||||
uint8 rm_bcd = buf.qbuf[3];
|
||||
uint8 rs_bcd = buf.qbuf[4];
|
||||
uint8 rf_bcd = buf.qbuf[5];
|
||||
uint8 am_bcd = buf.qbuf[7];
|
||||
uint8 as_bcd = buf.qbuf[8];
|
||||
uint8 af_bcd = buf.qbuf[9];
|
||||
|
||||
//printf("%2x %2x %2x\n", am_bcd, as_bcd, af_bcd);
|
||||
|
||||
if(!BCD_is_valid(track_bcd) || !BCD_is_valid(index_bcd) || !BCD_is_valid(rm_bcd) || !BCD_is_valid(rs_bcd) || !BCD_is_valid(rf_bcd) ||
|
||||
!BCD_is_valid(am_bcd) || !BCD_is_valid(as_bcd) || !BCD_is_valid(af_bcd) ||
|
||||
rs_bcd > 0x59 || rf_bcd > 0x74 || as_bcd > 0x59 || af_bcd > 0x74)
|
||||
{
|
||||
#ifdef CHECK_CCD_GARBAGE_SUBQ_A
|
||||
throw MDFN_Error(0, _("Garbage subchannel Q data detected(bad BCD/out of range): %02x:%02x:%02x %02x:%02x:%02x"), rm_bcd, rs_bcd, rf_bcd, am_bcd, as_bcd, af_bcd);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
int lba = ((BCD_to_U8(am_bcd) * 60 + BCD_to_U8(as_bcd)) * 75 + BCD_to_U8(af_bcd)) - 150;
|
||||
uint8 track = BCD_to_U8(track_bcd);
|
||||
|
||||
#ifdef CHECK_CCD_GARBAGE_SUBQ_B
|
||||
if(prev_lba != INT_MAX && abs(lba - prev_lba) > 100)
|
||||
throw MDFN_Error(0, _("Garbage subchannel Q data detected(excessively large jump in AMSF)"));
|
||||
#endif
|
||||
|
||||
#ifdef CHECK_CCD_GARBAGE_SUBQ_C
|
||||
if(abs(lba - (int)s) > 100) //zero 19-jun-2015 a bit of a sneaky signed/unsigned fixup here
|
||||
throw MDFN_Error(0, _("Garbage subchannel Q data detected(AMSF value is out of tolerance)"));
|
||||
#endif
|
||||
|
||||
prev_lba = lba;
|
||||
|
||||
#ifdef CHECK_CCD_GARBAGE_SUBQ_D
|
||||
if(track < prev_track)
|
||||
throw MDFN_Error(0, _("Garbage subchannel Q data detected(bad track number)"));
|
||||
#endif
|
||||
//else if(prev_track && track - pre
|
||||
|
||||
prev_track = track;
|
||||
}
|
||||
checksum_pass_counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//printf("%u/%u\n", checksum_pass_counter, img_numsectors);
|
||||
}
|
||||
|
||||
CDAccess_CCD::~CDAccess_CCD()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void CDAccess_CCD::Read_Raw_Sector(uint8 *buf, int32 lba)
|
||||
{
|
||||
if(lba < 0)
|
||||
{
|
||||
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);
|
||||
|
||||
subpw_interleave(&sub_data[lba * 96], buf + 2352);
|
||||
}
|
||||
|
||||
bool CDAccess_CCD::Fast_Read_Raw_PW_TSRE(uint8* pwbuf, int32 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;
|
||||
}
|
||||
|
||||
void CDAccess_CCD::Read_TOC(CDUtility::TOC *toc)
|
||||
{
|
||||
*toc = tocd;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,50 +1,48 @@
|
|||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "../FileStream.h"
|
||||
#include "../MemoryStream.h"
|
||||
#include "CDAccess.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
class CDAccess_CCD : public CDAccess
|
||||
{
|
||||
public:
|
||||
|
||||
CDAccess_CCD(const std::string& path, bool image_memcache);
|
||||
virtual ~CDAccess_CCD();
|
||||
|
||||
virtual void Read_Raw_Sector(uint8 *buf, int32 lba);
|
||||
|
||||
virtual void Read_TOC(CDUtility::TOC *toc);
|
||||
|
||||
virtual bool Is_Physical(void) throw();
|
||||
|
||||
virtual void Eject(bool eject_status);
|
||||
|
||||
private:
|
||||
|
||||
void Load(const std::string& path, bool image_memcache);
|
||||
void Cleanup(void);
|
||||
|
||||
void CheckSubQSanity(void);
|
||||
|
||||
Stream* img_stream;
|
||||
Stream* sub_stream;
|
||||
size_t img_numsectors;
|
||||
CDUtility::TOC tocd;
|
||||
};
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "../FileStream.h"
|
||||
#include "../MemoryStream.h"
|
||||
#include "CDAccess.h"
|
||||
#include <memory>
|
||||
|
||||
class CDAccess_CCD : public CDAccess
|
||||
{
|
||||
public:
|
||||
|
||||
CDAccess_CCD(const std::string& path, bool image_memcache);
|
||||
virtual ~CDAccess_CCD();
|
||||
|
||||
virtual void Read_Raw_Sector(uint8 *buf, int32 lba);
|
||||
|
||||
virtual bool Fast_Read_Raw_PW_TSRE(uint8* pwbuf, int32 lba) const noexcept;
|
||||
|
||||
virtual void Read_TOC(CDUtility::TOC *toc);
|
||||
|
||||
private:
|
||||
|
||||
void Load(const std::string& path, bool image_memcache);
|
||||
void Cleanup(void);
|
||||
|
||||
void CheckSubQSanity(void);
|
||||
|
||||
std::unique_ptr<Stream> img_stream;
|
||||
std::unique_ptr<uint8[]> sub_data;
|
||||
|
||||
size_t img_numsectors;
|
||||
CDUtility::TOC tocd;
|
||||
};
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,102 +1,103 @@
|
|||
#ifndef __MDFN_CDACCESS_IMAGE_H
|
||||
#define __MDFN_CDACCESS_IMAGE_H
|
||||
|
||||
#include <map>
|
||||
#include <array>
|
||||
|
||||
class Stream;
|
||||
class AudioReader;
|
||||
|
||||
struct CDRFILE_TRACK_INFO
|
||||
{
|
||||
int32 LBA;
|
||||
|
||||
uint32 DIFormat;
|
||||
uint8 subq_control;
|
||||
|
||||
int32 pregap;
|
||||
int32 pregap_dv;
|
||||
|
||||
int32 postgap;
|
||||
|
||||
int32 index[2];
|
||||
|
||||
int32 sectors; // Not including pregap sectors!
|
||||
Stream *fp;
|
||||
bool FirstFileInstance;
|
||||
bool RawAudioMSBFirst;
|
||||
long FileOffset;
|
||||
unsigned int SubchannelMode;
|
||||
|
||||
uint32 LastSamplePos;
|
||||
|
||||
AudioReader *AReader;
|
||||
};
|
||||
#if 0
|
||||
struct Medium_Chunk
|
||||
{
|
||||
int64 Offset; // Offset in [..TODO..]
|
||||
uint32 DIFormat;
|
||||
|
||||
FILE *fp;
|
||||
bool FirstFileInstance;
|
||||
bool RawAudioMSBFirst;
|
||||
unsigned int SubchannelMode;
|
||||
|
||||
uint32 LastSamplePos;
|
||||
AudioReader *AReader;
|
||||
};
|
||||
|
||||
struct CD_Chunk
|
||||
{
|
||||
int32 LBA;
|
||||
int32 Track;
|
||||
int32 Index;
|
||||
bool DataType;
|
||||
|
||||
Medium_Chunk Medium;
|
||||
};
|
||||
|
||||
static std::vector<CD_Chunk> Chunks;
|
||||
#endif
|
||||
|
||||
class CDAccess_Image : public CDAccess
|
||||
{
|
||||
public:
|
||||
|
||||
CDAccess_Image(const std::string& path, bool image_memcache);
|
||||
virtual ~CDAccess_Image();
|
||||
|
||||
virtual void Read_Raw_Sector(uint8 *buf, int32 lba);
|
||||
|
||||
virtual void Read_TOC(CDUtility::TOC *toc);
|
||||
|
||||
virtual bool Is_Physical(void) throw();
|
||||
|
||||
virtual void Eject(bool eject_status);
|
||||
private:
|
||||
|
||||
int32 NumTracks;
|
||||
int32 FirstTrack;
|
||||
int32 LastTrack;
|
||||
int32 total_sectors;
|
||||
uint8 disc_type;
|
||||
CDRFILE_TRACK_INFO Tracks[100]; // Track #0(HMM?) through 99
|
||||
|
||||
std::map<uint32, std::array<uint8, 12>> SubQReplaceMap;
|
||||
|
||||
std::string base_dir;
|
||||
|
||||
void ImageOpen(const std::string& path, bool image_memcache);
|
||||
void LoadSBI(const std::string& sbi_path);
|
||||
void Cleanup(void);
|
||||
|
||||
// MakeSubPQ will OR the simulated P and Q subchannel data into SubPWBuf.
|
||||
void MakeSubPQ(int32 lba, uint8 *SubPWBuf);
|
||||
|
||||
void ParseTOCFileLineInfo(CDRFILE_TRACK_INFO *track, const int tracknum, const std::string &filename, const char *binoffset, const char *msfoffset, const char *length, bool image_memcache, std::map<std::string, Stream*> &toc_streamcache);
|
||||
uint32 GetSectorCount(CDRFILE_TRACK_INFO *track);
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
#ifndef __MDFN_CDACCESS_IMAGE_H
|
||||
#define __MDFN_CDACCESS_IMAGE_H
|
||||
|
||||
#include <map>
|
||||
#include <array>
|
||||
|
||||
class Stream;
|
||||
class CDAFReader;
|
||||
|
||||
struct CDRFILE_TRACK_INFO
|
||||
{
|
||||
int32 LBA;
|
||||
|
||||
uint32 DIFormat;
|
||||
uint8 subq_control;
|
||||
|
||||
int32 pregap;
|
||||
int32 pregap_dv;
|
||||
|
||||
int32 postgap;
|
||||
|
||||
int32 index[2];
|
||||
|
||||
int32 sectors; // Not including pregap sectors!
|
||||
Stream *fp;
|
||||
bool FirstFileInstance;
|
||||
bool RawAudioMSBFirst;
|
||||
long FileOffset;
|
||||
unsigned int SubchannelMode;
|
||||
|
||||
uint32 LastSamplePos;
|
||||
|
||||
CDAFReader *AReader;
|
||||
};
|
||||
#if 0
|
||||
struct Medium_Chunk
|
||||
{
|
||||
int64 Offset; // Offset in [..TODO..]
|
||||
uint32 DIFormat;
|
||||
|
||||
FILE *fp;
|
||||
bool FirstFileInstance;
|
||||
bool RawAudioMSBFirst;
|
||||
unsigned int SubchannelMode;
|
||||
|
||||
uint32 LastSamplePos;
|
||||
AudioReader *AReader;
|
||||
};
|
||||
|
||||
struct CD_Chunk
|
||||
{
|
||||
int32 LBA;
|
||||
int32 Track;
|
||||
int32 Index;
|
||||
bool DataType;
|
||||
|
||||
Medium_Chunk Medium;
|
||||
};
|
||||
|
||||
static std::vector<CD_Chunk> Chunks;
|
||||
#endif
|
||||
|
||||
class CDAccess_Image : public CDAccess
|
||||
{
|
||||
public:
|
||||
|
||||
CDAccess_Image(const std::string& path, bool image_memcache);
|
||||
virtual ~CDAccess_Image();
|
||||
|
||||
virtual void Read_Raw_Sector(uint8 *buf, int32 lba);
|
||||
|
||||
virtual bool Fast_Read_Raw_PW_TSRE(uint8* pwbuf, int32 lba) const noexcept;
|
||||
|
||||
virtual void Read_TOC(CDUtility::TOC *toc);
|
||||
|
||||
private:
|
||||
|
||||
int32 NumTracks;
|
||||
int32 FirstTrack;
|
||||
int32 LastTrack;
|
||||
int32 total_sectors;
|
||||
uint8 disc_type;
|
||||
CDRFILE_TRACK_INFO Tracks[100]; // Track #0(HMM?) through 99
|
||||
CDUtility::TOC toc;
|
||||
|
||||
std::map<uint32, std::array<uint8, 12>> SubQReplaceMap;
|
||||
|
||||
std::string base_dir;
|
||||
|
||||
void ImageOpen(const std::string& path, bool image_memcache);
|
||||
void LoadSBI(const std::string& sbi_path);
|
||||
void GenerateTOC(void);
|
||||
void Cleanup(void);
|
||||
|
||||
// MakeSubPQ will OR the simulated P and Q subchannel data into SubPWBuf.
|
||||
int32 MakeSubPQ(int32 lba, uint8 *SubPWBuf) const;
|
||||
|
||||
void ParseTOCFileLineInfo(CDRFILE_TRACK_INFO *track, const int tracknum, const std::string &filename, const char *binoffset, const char *msfoffset, const char *length, bool image_memcache, std::map<std::string, Stream*> &toc_streamcache);
|
||||
uint32 GetSectorCount(CDRFILE_TRACK_INFO *track);
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,324 +1,435 @@
|
|||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* Subchannel Q CRC Code: Copyright (C) 1998 Andreas Mueller <mueller@daneb.ping.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* Subchannel Q CRC Code: Copyright (C) 1998 Andreas Mueller <mueller@daneb.ping.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "emuware/emuware.h"
|
||||
#include "CDUtility.h"
|
||||
#include "dvdisaster.h"
|
||||
#include "lec.h"
|
||||
|
||||
// Kill_LEC_Correct();
|
||||
|
||||
|
||||
namespace CDUtility
|
||||
{
|
||||
|
||||
// lookup table for crc calculation
|
||||
static uint16 subq_crctab[256] =
|
||||
{
|
||||
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108,
|
||||
0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210,
|
||||
0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B,
|
||||
0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
|
||||
0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE,
|
||||
0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6,
|
||||
0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D,
|
||||
0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
|
||||
0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5,
|
||||
0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC,
|
||||
0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4,
|
||||
0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
|
||||
0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13,
|
||||
0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A,
|
||||
0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E,
|
||||
0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
|
||||
0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1,
|
||||
0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB,
|
||||
0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0,
|
||||
0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
|
||||
0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657,
|
||||
0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9,
|
||||
0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882,
|
||||
0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
|
||||
0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E,
|
||||
0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07,
|
||||
0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D,
|
||||
0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
|
||||
0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
|
||||
};
|
||||
|
||||
|
||||
static uint8 scramble_table[2352 - 12];
|
||||
|
||||
static bool CDUtility_Inited = false;
|
||||
|
||||
static void InitScrambleTable(void)
|
||||
{
|
||||
unsigned cv = 1;
|
||||
|
||||
for(unsigned i = 12; i < 2352; i++)
|
||||
{
|
||||
unsigned char z = 0;
|
||||
|
||||
for(int b = 0; b < 8; b++)
|
||||
{
|
||||
z |= (cv & 1) << b;
|
||||
|
||||
int feedback = ((cv >> 1) & 1) ^ (cv & 1);
|
||||
cv = (cv >> 1) | (feedback << 14);
|
||||
}
|
||||
|
||||
scramble_table[i - 12] = z;
|
||||
}
|
||||
|
||||
//for(int i = 0; i < 2352 - 12; i++)
|
||||
// printf("0x%02x, ", scramble_table[i]);
|
||||
}
|
||||
|
||||
void CDUtility_Init(void)
|
||||
{
|
||||
if(!CDUtility_Inited)
|
||||
{
|
||||
Init_LEC_Correct();
|
||||
|
||||
InitScrambleTable();
|
||||
|
||||
CDUtility_Inited = true;
|
||||
}
|
||||
}
|
||||
|
||||
void encode_mode0_sector(uint32 aba, uint8 *sector_data)
|
||||
{
|
||||
CDUtility_Init();
|
||||
|
||||
lec_encode_mode0_sector(aba, sector_data);
|
||||
}
|
||||
|
||||
void encode_mode1_sector(uint32 aba, uint8 *sector_data)
|
||||
{
|
||||
CDUtility_Init();
|
||||
|
||||
lec_encode_mode1_sector(aba, sector_data);
|
||||
}
|
||||
|
||||
void encode_mode2_sector(uint32 aba, uint8 *sector_data)
|
||||
{
|
||||
CDUtility_Init();
|
||||
|
||||
lec_encode_mode2_sector(aba, sector_data);
|
||||
}
|
||||
|
||||
void encode_mode2_form1_sector(uint32 aba, uint8 *sector_data)
|
||||
{
|
||||
CDUtility_Init();
|
||||
|
||||
lec_encode_mode2_form1_sector(aba, sector_data);
|
||||
}
|
||||
|
||||
void encode_mode2_form2_sector(uint32 aba, uint8 *sector_data)
|
||||
{
|
||||
CDUtility_Init();
|
||||
|
||||
lec_encode_mode2_form2_sector(aba, sector_data);
|
||||
}
|
||||
|
||||
bool edc_check(const uint8 *sector_data, bool xa)
|
||||
{
|
||||
CDUtility_Init();
|
||||
|
||||
return(CheckEDC(sector_data, xa));
|
||||
}
|
||||
|
||||
bool edc_lec_check_and_correct(uint8 *sector_data, bool xa)
|
||||
{
|
||||
CDUtility_Init();
|
||||
|
||||
return(ValidateRawSector(sector_data, xa));
|
||||
}
|
||||
|
||||
|
||||
bool subq_check_checksum(const uint8 *SubQBuf)
|
||||
{
|
||||
uint16 crc = 0;
|
||||
uint16 stored_crc = 0;
|
||||
|
||||
stored_crc = SubQBuf[0xA] << 8;
|
||||
stored_crc |= SubQBuf[0xB];
|
||||
|
||||
for(int i = 0; i < 0xA; i++)
|
||||
crc = subq_crctab[(crc >> 8) ^ SubQBuf[i]] ^ (crc << 8);
|
||||
|
||||
crc = ~crc;
|
||||
|
||||
return(crc == stored_crc);
|
||||
}
|
||||
|
||||
void subq_generate_checksum(uint8 *buf)
|
||||
{
|
||||
uint16 crc = 0;
|
||||
|
||||
for(int i = 0; i < 0xA; i++)
|
||||
crc = subq_crctab[(crc >> 8) ^ buf[i]] ^ (crc << 8);
|
||||
|
||||
// Checksum
|
||||
buf[0xa] = ~(crc >> 8);
|
||||
buf[0xb] = ~(crc);
|
||||
}
|
||||
|
||||
void subq_deinterleave(const uint8 *SubPWBuf, uint8 *qbuf)
|
||||
{
|
||||
memset(qbuf, 0, 0xC);
|
||||
|
||||
for(int i = 0; i < 96; i++)
|
||||
{
|
||||
qbuf[i >> 3] |= ((SubPWBuf[i] >> 6) & 0x1) << (7 - (i & 0x7));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Deinterleaves 96 bytes of subchannel P-W data from 96 bytes of interleaved subchannel PW data.
|
||||
void subpw_deinterleave(const uint8 *in_buf, uint8 *out_buf)
|
||||
{
|
||||
assert(in_buf != out_buf);
|
||||
|
||||
memset(out_buf, 0, 96);
|
||||
|
||||
for(unsigned ch = 0; ch < 8; ch++)
|
||||
{
|
||||
for(unsigned i = 0; i < 96; i++)
|
||||
{
|
||||
out_buf[(ch * 12) + (i >> 3)] |= ((in_buf[i] >> (7 - ch)) & 0x1) << (7 - (i & 0x7));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Interleaves 96 bytes of subchannel P-W data from 96 bytes of uninterleaved subchannel PW data.
|
||||
void subpw_interleave(const uint8 *in_buf, uint8 *out_buf)
|
||||
{
|
||||
assert(in_buf != out_buf);
|
||||
|
||||
for(unsigned d = 0; d < 12; d++)
|
||||
{
|
||||
for(unsigned bitpoodle = 0; bitpoodle < 8; bitpoodle++)
|
||||
{
|
||||
uint8 rawb = 0;
|
||||
|
||||
for(unsigned ch = 0; ch < 8; ch++)
|
||||
{
|
||||
rawb |= ((in_buf[ch * 12 + d] >> (7 - bitpoodle)) & 1) << (7 - ch);
|
||||
}
|
||||
out_buf[(d << 3) + bitpoodle] = rawb;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOTES ON LEADOUT AREA SYNTHESIS
|
||||
//
|
||||
// I'm not trusting that the "control" field for the TOC leadout entry will always be set properly, so | the control fields for the last track entry
|
||||
// and the leadout entry together before extracting the D2 bit. Audio track->data leadout is fairly benign though maybe noisy(especially if we ever implement
|
||||
// data scrambling properly), but data track->audio leadout could break things in an insidious manner for the more accurate drive emulation code).
|
||||
//
|
||||
void subpw_synth_leadout_lba(const TOC& toc, const int32 lba, uint8* SubPWBuf)
|
||||
{
|
||||
uint8 buf[0xC];
|
||||
uint32 lba_relative;
|
||||
uint32 ma, sa, fa;
|
||||
uint32 m, s, f;
|
||||
|
||||
lba_relative = lba - toc.tracks[100].lba;
|
||||
|
||||
f = (lba_relative % 75);
|
||||
s = ((lba_relative / 75) % 60);
|
||||
m = (lba_relative / 75 / 60);
|
||||
|
||||
fa = (lba + 150) % 75;
|
||||
sa = ((lba + 150) / 75) % 60;
|
||||
ma = ((lba + 150) / 75 / 60);
|
||||
|
||||
uint8 adr = 0x1; // Q channel data encodes position
|
||||
uint8 control = (toc.tracks[toc.last_track].control & 0x4) | toc.tracks[100].control;
|
||||
|
||||
memset(buf, 0, 0xC);
|
||||
buf[0] = (adr << 0) | (control << 4);
|
||||
buf[1] = 0xAA;
|
||||
buf[2] = 0x01;
|
||||
|
||||
// Track relative MSF address
|
||||
buf[3] = U8_to_BCD(m);
|
||||
buf[4] = U8_to_BCD(s);
|
||||
buf[5] = U8_to_BCD(f);
|
||||
|
||||
buf[6] = 0; // Zerroooo
|
||||
|
||||
// Absolute MSF address
|
||||
buf[7] = U8_to_BCD(ma);
|
||||
buf[8] = U8_to_BCD(sa);
|
||||
buf[9] = U8_to_BCD(fa);
|
||||
|
||||
subq_generate_checksum(buf);
|
||||
|
||||
for(int i = 0; i < 96; i++)
|
||||
SubPWBuf[i] = (((buf[i >> 3] >> (7 - (i & 0x7))) & 1) ? 0x40 : 0x00) | 0x80;
|
||||
}
|
||||
|
||||
void synth_leadout_sector_lba(const uint8 mode, const TOC& toc, const int32 lba, uint8* out_buf)
|
||||
{
|
||||
memset(out_buf, 0, 2352 + 96);
|
||||
subpw_synth_leadout_lba(toc, lba, out_buf + 2352);
|
||||
|
||||
if((toc.tracks[toc.last_track].control | toc.tracks[100].control) & 0x4)
|
||||
{
|
||||
switch(mode)
|
||||
{
|
||||
default:
|
||||
encode_mode0_sector(LBA_to_ABA(lba), out_buf);
|
||||
break;
|
||||
|
||||
case 0x01:
|
||||
encode_mode1_sector(LBA_to_ABA(lba), out_buf);
|
||||
break;
|
||||
|
||||
case 0x02:
|
||||
out_buf[18] = 0x20;
|
||||
encode_mode2_form2_sector(LBA_to_ABA(lba), out_buf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
bool subq_extrapolate(const uint8 *subq_input, int32 position_delta, uint8 *subq_output)
|
||||
{
|
||||
assert(subq_check_checksum(subq_input));
|
||||
|
||||
|
||||
subq_generate_checksum(subq_output);
|
||||
}
|
||||
#endif
|
||||
|
||||
void scrambleize_data_sector(uint8 *sector_data)
|
||||
{
|
||||
for(unsigned i = 12; i < 2352; i++)
|
||||
sector_data[i] ^= scramble_table[i - 12];
|
||||
}
|
||||
|
||||
}
|
||||
#include "emuware/emuware.h"
|
||||
#include "CDUtility.h"
|
||||
#include "dvdisaster.h"
|
||||
#include "lec.h"
|
||||
|
||||
#include <assert.h>
|
||||
// Kill_LEC_Correct();
|
||||
|
||||
|
||||
namespace CDUtility
|
||||
{
|
||||
|
||||
// lookup table for crc calculation
|
||||
static uint16 subq_crctab[256] =
|
||||
{
|
||||
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108,
|
||||
0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210,
|
||||
0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B,
|
||||
0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
|
||||
0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE,
|
||||
0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6,
|
||||
0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D,
|
||||
0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
|
||||
0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5,
|
||||
0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC,
|
||||
0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4,
|
||||
0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
|
||||
0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13,
|
||||
0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A,
|
||||
0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E,
|
||||
0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
|
||||
0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1,
|
||||
0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB,
|
||||
0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0,
|
||||
0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
|
||||
0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657,
|
||||
0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9,
|
||||
0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882,
|
||||
0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
|
||||
0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E,
|
||||
0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07,
|
||||
0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D,
|
||||
0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
|
||||
0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
|
||||
};
|
||||
|
||||
|
||||
static uint8 scramble_table[2352 - 12];
|
||||
|
||||
static bool CDUtility_Inited = false;
|
||||
|
||||
static void InitScrambleTable(void)
|
||||
{
|
||||
unsigned cv = 1;
|
||||
|
||||
for(unsigned i = 12; i < 2352; i++)
|
||||
{
|
||||
unsigned char z = 0;
|
||||
|
||||
for(int b = 0; b < 8; b++)
|
||||
{
|
||||
z |= (cv & 1) << b;
|
||||
|
||||
int feedback = ((cv >> 1) & 1) ^ (cv & 1);
|
||||
cv = (cv >> 1) | (feedback << 14);
|
||||
}
|
||||
|
||||
scramble_table[i - 12] = z;
|
||||
}
|
||||
|
||||
//for(int i = 0; i < 2352 - 12; i++)
|
||||
// printf("0x%02x, ", scramble_table[i]);
|
||||
}
|
||||
|
||||
void CDUtility_Init(void)
|
||||
{
|
||||
if(!CDUtility_Inited)
|
||||
{
|
||||
Init_LEC_Correct();
|
||||
|
||||
InitScrambleTable();
|
||||
|
||||
CDUtility_Inited = true;
|
||||
}
|
||||
}
|
||||
|
||||
void encode_mode0_sector(uint32 aba, uint8 *sector_data)
|
||||
{
|
||||
CDUtility_Init();
|
||||
|
||||
lec_encode_mode0_sector(aba, sector_data);
|
||||
}
|
||||
|
||||
void encode_mode1_sector(uint32 aba, uint8 *sector_data)
|
||||
{
|
||||
CDUtility_Init();
|
||||
|
||||
lec_encode_mode1_sector(aba, sector_data);
|
||||
}
|
||||
|
||||
void encode_mode2_sector(uint32 aba, uint8 *sector_data)
|
||||
{
|
||||
CDUtility_Init();
|
||||
|
||||
lec_encode_mode2_sector(aba, sector_data);
|
||||
}
|
||||
|
||||
void encode_mode2_form1_sector(uint32 aba, uint8 *sector_data)
|
||||
{
|
||||
CDUtility_Init();
|
||||
|
||||
lec_encode_mode2_form1_sector(aba, sector_data);
|
||||
}
|
||||
|
||||
void encode_mode2_form2_sector(uint32 aba, uint8 *sector_data)
|
||||
{
|
||||
CDUtility_Init();
|
||||
|
||||
lec_encode_mode2_form2_sector(aba, sector_data);
|
||||
}
|
||||
|
||||
bool edc_check(const uint8 *sector_data, bool xa)
|
||||
{
|
||||
CDUtility_Init();
|
||||
|
||||
return(CheckEDC(sector_data, xa));
|
||||
}
|
||||
|
||||
bool edc_lec_check_and_correct(uint8 *sector_data, bool xa)
|
||||
{
|
||||
CDUtility_Init();
|
||||
|
||||
return(ValidateRawSector(sector_data, xa));
|
||||
}
|
||||
|
||||
|
||||
bool subq_check_checksum(const uint8 *SubQBuf)
|
||||
{
|
||||
uint16 crc = 0;
|
||||
uint16 stored_crc = 0;
|
||||
|
||||
stored_crc = SubQBuf[0xA] << 8;
|
||||
stored_crc |= SubQBuf[0xB];
|
||||
|
||||
for(int i = 0; i < 0xA; i++)
|
||||
crc = subq_crctab[(crc >> 8) ^ SubQBuf[i]] ^ (crc << 8);
|
||||
|
||||
crc = ~crc;
|
||||
|
||||
return(crc == stored_crc);
|
||||
}
|
||||
|
||||
void subq_generate_checksum(uint8 *buf)
|
||||
{
|
||||
uint16 crc = 0;
|
||||
|
||||
for(int i = 0; i < 0xA; i++)
|
||||
crc = subq_crctab[(crc >> 8) ^ buf[i]] ^ (crc << 8);
|
||||
|
||||
// Checksum
|
||||
buf[0xa] = ~(crc >> 8);
|
||||
buf[0xb] = ~(crc);
|
||||
}
|
||||
|
||||
void subq_deinterleave(const uint8 *SubPWBuf, uint8 *qbuf)
|
||||
{
|
||||
memset(qbuf, 0, 0xC);
|
||||
|
||||
for(int i = 0; i < 96; i++)
|
||||
{
|
||||
qbuf[i >> 3] |= ((SubPWBuf[i] >> 6) & 0x1) << (7 - (i & 0x7));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Deinterleaves 96 bytes of subchannel P-W data from 96 bytes of interleaved subchannel PW data.
|
||||
void subpw_deinterleave(const uint8 *in_buf, uint8 *out_buf)
|
||||
{
|
||||
assert(in_buf != out_buf);
|
||||
|
||||
memset(out_buf, 0, 96);
|
||||
|
||||
for(unsigned ch = 0; ch < 8; ch++)
|
||||
{
|
||||
for(unsigned i = 0; i < 96; i++)
|
||||
{
|
||||
out_buf[(ch * 12) + (i >> 3)] |= ((in_buf[i] >> (7 - ch)) & 0x1) << (7 - (i & 0x7));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Interleaves 96 bytes of subchannel P-W data from 96 bytes of uninterleaved subchannel PW data.
|
||||
void subpw_interleave(const uint8 *in_buf, uint8 *out_buf)
|
||||
{
|
||||
assert(in_buf != out_buf);
|
||||
|
||||
for(unsigned d = 0; d < 12; d++)
|
||||
{
|
||||
for(unsigned bitpoodle = 0; bitpoodle < 8; bitpoodle++)
|
||||
{
|
||||
uint8 rawb = 0;
|
||||
|
||||
for(unsigned ch = 0; ch < 8; ch++)
|
||||
{
|
||||
rawb |= ((in_buf[ch * 12 + d] >> (7 - bitpoodle)) & 1) << (7 - ch);
|
||||
}
|
||||
out_buf[(d << 3) + bitpoodle] = rawb;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOTES ON LEADOUT AREA SYNTHESIS
|
||||
//
|
||||
// I'm not trusting that the "control" field for the TOC leadout entry will always be set properly, so | the control fields for the last track entry
|
||||
// and the leadout entry together before extracting the D2 bit. Audio track->data leadout is fairly benign though maybe noisy(especially if we ever implement
|
||||
// data scrambling properly), but data track->audio leadout could break things in an insidious manner for the more accurate drive emulation code).
|
||||
//
|
||||
void subpw_synth_leadout_lba(const TOC& toc, const int32 lba, uint8* SubPWBuf)
|
||||
{
|
||||
uint8 buf[0xC];
|
||||
uint32 lba_relative;
|
||||
uint32 ma, sa, fa;
|
||||
uint32 m, s, f;
|
||||
|
||||
lba_relative = lba - toc.tracks[100].lba;
|
||||
|
||||
f = (lba_relative % 75);
|
||||
s = ((lba_relative / 75) % 60);
|
||||
m = (lba_relative / 75 / 60);
|
||||
|
||||
fa = (lba + 150) % 75;
|
||||
sa = ((lba + 150) / 75) % 60;
|
||||
ma = ((lba + 150) / 75 / 60);
|
||||
|
||||
uint8 adr = 0x1; // Q channel data encodes position
|
||||
uint8 control = toc.tracks[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);
|
||||
buf[0] = (adr << 0) | (control << 4);
|
||||
buf[1] = 0xAA;
|
||||
buf[2] = 0x01;
|
||||
|
||||
// Track relative MSF address
|
||||
buf[3] = U8_to_BCD(m);
|
||||
buf[4] = U8_to_BCD(s);
|
||||
buf[5] = U8_to_BCD(f);
|
||||
|
||||
buf[6] = 0; // Zerroooo
|
||||
|
||||
// Absolute MSF address
|
||||
buf[7] = U8_to_BCD(ma);
|
||||
buf[8] = U8_to_BCD(sa);
|
||||
buf[9] = U8_to_BCD(fa);
|
||||
|
||||
subq_generate_checksum(buf);
|
||||
|
||||
for(int i = 0; i < 96; i++)
|
||||
SubPWBuf[i] = (((buf[i >> 3] >> (7 - (i & 0x7))) & 1) ? 0x40 : 0x00) | 0x80;
|
||||
}
|
||||
|
||||
void synth_leadout_sector_lba(uint8 mode, const TOC& toc, const int32 lba, uint8* out_buf)
|
||||
{
|
||||
memset(out_buf, 0, 2352 + 96);
|
||||
subpw_synth_leadout_lba(toc, lba, out_buf + 2352);
|
||||
|
||||
if(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
|
||||
//
|
||||
void subpw_synth_udapp_lba(const TOC& toc, const int32 lba, const int32 lba_subq_relative_offs, uint8* SubPWBuf)
|
||||
{
|
||||
uint8 buf[0xC];
|
||||
uint32 lba_relative;
|
||||
uint32 ma, sa, fa;
|
||||
uint32 m, s, f;
|
||||
|
||||
if(lba < -150 || lba >= 0)
|
||||
printf("[BUG] subpw_synth_udapp_lba() lba out of range --- %d\n", lba);
|
||||
|
||||
{
|
||||
int32 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 adr = 0x1; // Q channel data encodes position
|
||||
uint8 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 mode, const TOC& toc, const int32 lba, int32 lba_subq_relative_offs, uint8* 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
bool subq_extrapolate(const uint8 *subq_input, int32 position_delta, uint8 *subq_output)
|
||||
{
|
||||
assert(subq_check_checksum(subq_input));
|
||||
|
||||
|
||||
subq_generate_checksum(subq_output);
|
||||
}
|
||||
#endif
|
||||
|
||||
void scrambleize_data_sector(uint8 *sector_data)
|
||||
{
|
||||
for(unsigned i = 12; i < 2352; i++)
|
||||
sector_data[i] ^= scramble_table[i - 12];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,225 +1,234 @@
|
|||
#ifndef __MDFN_CDROM_CDUTILITY_H
|
||||
#define __MDFN_CDROM_CDUTILITY_H
|
||||
|
||||
namespace CDUtility
|
||||
{
|
||||
// Call once at app startup before creating any threads that could potentially cause re-entrancy to these functions.
|
||||
// It will also be called automatically if needed for the first time a function in this namespace that requires
|
||||
// the initialization function to be called is called, for potential
|
||||
// usage in constructors of statically-declared objects.
|
||||
void CDUtility_Init(void);
|
||||
|
||||
// Quick definitions here:
|
||||
//
|
||||
// ABA - Absolute block address, synonymous to absolute MSF
|
||||
// aba = (m_a * 60 * 75) + (s_a * 75) + f_a
|
||||
//
|
||||
// LBA - Logical block address(related: data CDs are required to have a pregap of 2 seconds, IE 150 frames/sectors)
|
||||
// lba = aba - 150
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
ADR_NOQINFO = 0x00,
|
||||
ADR_CURPOS = 0x01,
|
||||
ADR_MCN = 0x02,
|
||||
ADR_ISRC = 0x03
|
||||
};
|
||||
|
||||
|
||||
struct TOC_Track
|
||||
{
|
||||
uint8 adr;
|
||||
uint8 control;
|
||||
uint32 lba;
|
||||
};
|
||||
|
||||
// SubQ control field flags.
|
||||
enum
|
||||
{
|
||||
SUBQ_CTRLF_PRE = 0x01, // With 50/15us pre-emphasis.
|
||||
SUBQ_CTRLF_DCP = 0x02, // Digital copy permitted.
|
||||
SUBQ_CTRLF_DATA = 0x04, // Data track.
|
||||
SUBQ_CTRLF_4CH = 0x08, // 4-channel CD-DA.
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
DISC_TYPE_CDDA_OR_M1 = 0x00,
|
||||
DISC_TYPE_CD_I = 0x10,
|
||||
DISC_TYPE_CD_XA = 0x20
|
||||
};
|
||||
|
||||
struct TOC
|
||||
{
|
||||
INLINE TOC()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
INLINE void Clear(void)
|
||||
{
|
||||
first_track = last_track = 0;
|
||||
disc_type = 0;
|
||||
|
||||
memset(tracks, 0, sizeof(tracks)); // FIXME if we change TOC_Track to non-POD type.
|
||||
}
|
||||
|
||||
INLINE int FindTrackByLBA(uint32 LBA)
|
||||
{
|
||||
for(int32 track = first_track; track <= (last_track + 1); track++)
|
||||
{
|
||||
if(track == (last_track + 1))
|
||||
{
|
||||
if(LBA < tracks[100].lba)
|
||||
return(track - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(LBA < tracks[track].lba)
|
||||
return(track - 1);
|
||||
}
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
uint8 first_track;
|
||||
uint8 last_track;
|
||||
uint8 disc_type;
|
||||
TOC_Track tracks[100 + 1]; // [0] is unused, [100] is for the leadout track.
|
||||
// Also, for convenience, tracks[last_track + 1] will always refer
|
||||
// to the leadout track(even if last_track < 99, IE the leadout track details are duplicated).
|
||||
};
|
||||
|
||||
//
|
||||
// Address conversion functions.
|
||||
//
|
||||
static INLINE uint32 AMSF_to_ABA(int32 m_a, int32 s_a, int32 f_a)
|
||||
{
|
||||
return(f_a + 75 * s_a + 75 * 60 * m_a);
|
||||
}
|
||||
|
||||
static INLINE void ABA_to_AMSF(uint32 aba, uint8 *m_a, uint8 *s_a, uint8 *f_a)
|
||||
{
|
||||
*m_a = aba / 75 / 60;
|
||||
*s_a = (aba - *m_a * 75 * 60) / 75;
|
||||
*f_a = aba - (*m_a * 75 * 60) - (*s_a * 75);
|
||||
}
|
||||
|
||||
static INLINE int32 ABA_to_LBA(uint32 aba)
|
||||
{
|
||||
return(aba - 150);
|
||||
}
|
||||
|
||||
static INLINE uint32 LBA_to_ABA(int32 lba)
|
||||
{
|
||||
return(lba + 150);
|
||||
}
|
||||
|
||||
static INLINE int32 AMSF_to_LBA(uint8 m_a, uint8 s_a, uint8 f_a)
|
||||
{
|
||||
return(ABA_to_LBA(AMSF_to_ABA(m_a, s_a, f_a)));
|
||||
}
|
||||
|
||||
static INLINE void LBA_to_AMSF(int32 lba, uint8 *m_a, uint8 *s_a, uint8 *f_a)
|
||||
{
|
||||
ABA_to_AMSF(LBA_to_ABA(lba), m_a, s_a, f_a);
|
||||
}
|
||||
|
||||
//
|
||||
// BCD conversion functions
|
||||
//
|
||||
static INLINE bool BCD_is_valid(uint8 bcd_number)
|
||||
{
|
||||
if((bcd_number & 0xF0) >= 0xA0)
|
||||
return(false);
|
||||
|
||||
if((bcd_number & 0x0F) >= 0x0A)
|
||||
return(false);
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
static INLINE uint8 BCD_to_U8(uint8 bcd_number)
|
||||
{
|
||||
return( ((bcd_number >> 4) * 10) + (bcd_number & 0x0F) );
|
||||
}
|
||||
|
||||
static INLINE uint8 U8_to_BCD(uint8 num)
|
||||
{
|
||||
return( ((num / 10) << 4) + (num % 10) );
|
||||
}
|
||||
|
||||
// should always perform the conversion, even if the bcd number is invalid.
|
||||
static INLINE bool BCD_to_U8_check(uint8 bcd_number, uint8 *out_number)
|
||||
{
|
||||
*out_number = BCD_to_U8(bcd_number);
|
||||
|
||||
if(!BCD_is_valid(bcd_number))
|
||||
return(false);
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
//
|
||||
// Sector data encoding functions(to full 2352 bytes raw sector).
|
||||
//
|
||||
// sector_data must be able to contain at least 2352 bytes.
|
||||
void encode_mode0_sector(uint32 aba, uint8 *sector_data);
|
||||
void encode_mode1_sector(uint32 aba, uint8 *sector_data); // 2048 bytes of user data at offset 16
|
||||
void encode_mode2_sector(uint32 aba, uint8 *sector_data); // 2336 bytes of user data at offset 16
|
||||
void encode_mode2_form1_sector(uint32 aba, uint8 *sector_data); // 2048+8 bytes of user data at offset 16
|
||||
void encode_mode2_form2_sector(uint32 aba, uint8 *sector_data); // 2324+8 bytes of user data at offset 16
|
||||
|
||||
|
||||
// out_buf must be able to contain 2352+96 bytes.
|
||||
// "mode" is only used if(toc.tracks[100].control & 0x4)
|
||||
void synth_leadout_sector_lba(const uint8 mode, const TOC& toc, const int32 lba, uint8* out_buf);
|
||||
|
||||
//
|
||||
// User data error detection and correction
|
||||
//
|
||||
|
||||
// Check EDC of a mode 1 or mode 2 form 1 sector.
|
||||
// Returns "true" if checksum is ok(matches).
|
||||
// Returns "false" if checksum mismatch.
|
||||
// sector_data should contain 2352 bytes of raw sector data.
|
||||
bool edc_check(const uint8 *sector_data, bool xa);
|
||||
|
||||
// Check EDC and L-EC data of a mode 1 or mode 2 form 1 sector, and correct bit errors if any exist.
|
||||
// Returns "true" if errors weren't detected, or they were corrected succesfully.
|
||||
// Returns "false" if errors couldn't be corrected.
|
||||
// sector_data should contain 2352 bytes of raw sector data.
|
||||
bool edc_lec_check_and_correct(uint8 *sector_data, bool xa);
|
||||
|
||||
//
|
||||
// Subchannel(Q in particular) functions
|
||||
//
|
||||
|
||||
// Returns false on checksum mismatch, true on match.
|
||||
bool subq_check_checksum(const uint8 *subq_buf);
|
||||
|
||||
// Calculates the checksum of Q subchannel data(not including the checksum bytes of course ;)) from subq_buf, and stores it into the appropriate position
|
||||
// in subq_buf.
|
||||
void subq_generate_checksum(uint8 *subq_buf);
|
||||
|
||||
// Deinterleaves 12 bytes of subchannel Q data from 96 bytes of interleaved subchannel PW data.
|
||||
void subq_deinterleave(const uint8 *subpw_buf, uint8 *subq_buf);
|
||||
|
||||
// Deinterleaves 96 bytes of subchannel P-W data from 96 bytes of interleaved subchannel PW data.
|
||||
void subpw_deinterleave(const uint8 *in_buf, uint8 *out_buf);
|
||||
|
||||
// Interleaves 96 bytes of subchannel P-W data from 96 bytes of uninterleaved subchannel PW data.
|
||||
void subpw_interleave(const uint8 *in_buf, uint8 *out_buf);
|
||||
|
||||
// Extrapolates Q subchannel current position data from subq_input, with frame/sector delta position_delta, and writes to subq_output.
|
||||
// Only valid for ADR_CURPOS.
|
||||
// subq_input must pass subq_check_checksum().
|
||||
// TODO
|
||||
//void subq_extrapolate(const uint8 *subq_input, int32 position_delta, uint8 *subq_output);
|
||||
|
||||
// (De)Scrambles data sector.
|
||||
void scrambleize_data_sector(uint8 *sector_data);
|
||||
}
|
||||
|
||||
#endif
|
||||
#ifndef __MDFN_CDROM_CDUTILITY_H
|
||||
#define __MDFN_CDROM_CDUTILITY_H
|
||||
|
||||
namespace CDUtility
|
||||
{
|
||||
// Call once at app startup before creating any threads that could potentially cause re-entrancy to these functions.
|
||||
// It will also be called automatically if needed for the first time a function in this namespace that requires
|
||||
// the initialization function to be called is called, for potential
|
||||
// usage in constructors of statically-declared objects.
|
||||
void CDUtility_Init(void);
|
||||
|
||||
// Quick definitions here:
|
||||
//
|
||||
// ABA - Absolute block address, synonymous to absolute MSF
|
||||
// aba = (m_a * 60 * 75) + (s_a * 75) + f_a
|
||||
//
|
||||
// LBA - Logical block address(related: data CDs are required to have a pregap of 2 seconds, IE 150 frames/sectors)
|
||||
// lba = aba - 150
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
ADR_NOQINFO = 0x00,
|
||||
ADR_CURPOS = 0x01,
|
||||
ADR_MCN = 0x02,
|
||||
ADR_ISRC = 0x03
|
||||
};
|
||||
|
||||
|
||||
struct TOC_Track
|
||||
{
|
||||
uint8 adr;
|
||||
uint8 control;
|
||||
uint32 lba;
|
||||
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.
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
DISC_TYPE_CDDA_OR_M1 = 0x00,
|
||||
DISC_TYPE_CD_I = 0x10,
|
||||
DISC_TYPE_CD_XA = 0x20
|
||||
};
|
||||
|
||||
struct TOC
|
||||
{
|
||||
INLINE TOC()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
INLINE void Clear(void)
|
||||
{
|
||||
first_track = last_track = 0;
|
||||
disc_type = 0;
|
||||
|
||||
memset(tracks, 0, sizeof(tracks)); // FIXME if we change TOC_Track to non-POD type.
|
||||
}
|
||||
|
||||
INLINE int FindTrackByLBA(uint32 LBA) const
|
||||
{
|
||||
int32 lvt = 0;
|
||||
|
||||
for(int32 track = 1; track <= 100; track++)
|
||||
{
|
||||
if(!tracks[track].valid)
|
||||
continue;
|
||||
|
||||
if(LBA < tracks[track].lba)
|
||||
break;
|
||||
|
||||
lvt = track;
|
||||
}
|
||||
|
||||
return(lvt);
|
||||
}
|
||||
|
||||
uint8 first_track;
|
||||
uint8 last_track;
|
||||
uint8 disc_type;
|
||||
TOC_Track tracks[100 + 1]; // [0] is unused, [100] is for the leadout track.
|
||||
};
|
||||
|
||||
//
|
||||
// Address conversion functions.
|
||||
//
|
||||
static INLINE uint32 AMSF_to_ABA(int32 m_a, int32 s_a, int32 f_a)
|
||||
{
|
||||
return(f_a + 75 * s_a + 75 * 60 * m_a);
|
||||
}
|
||||
|
||||
static INLINE void ABA_to_AMSF(uint32 aba, uint8 *m_a, uint8 *s_a, uint8 *f_a)
|
||||
{
|
||||
*m_a = aba / 75 / 60;
|
||||
*s_a = (aba - *m_a * 75 * 60) / 75;
|
||||
*f_a = aba - (*m_a * 75 * 60) - (*s_a * 75);
|
||||
}
|
||||
|
||||
static INLINE int32 ABA_to_LBA(uint32 aba)
|
||||
{
|
||||
return(aba - 150);
|
||||
}
|
||||
|
||||
static INLINE uint32 LBA_to_ABA(int32 lba)
|
||||
{
|
||||
return(lba + 150);
|
||||
}
|
||||
|
||||
static INLINE int32 AMSF_to_LBA(uint8 m_a, uint8 s_a, uint8 f_a)
|
||||
{
|
||||
return(ABA_to_LBA(AMSF_to_ABA(m_a, s_a, f_a)));
|
||||
}
|
||||
|
||||
static INLINE void LBA_to_AMSF(int32 lba, uint8 *m_a, uint8 *s_a, uint8 *f_a)
|
||||
{
|
||||
ABA_to_AMSF(LBA_to_ABA(lba), m_a, s_a, f_a);
|
||||
}
|
||||
|
||||
//
|
||||
// BCD conversion functions
|
||||
//
|
||||
static INLINE bool BCD_is_valid(uint8 bcd_number)
|
||||
{
|
||||
if((bcd_number & 0xF0) >= 0xA0)
|
||||
return(false);
|
||||
|
||||
if((bcd_number & 0x0F) >= 0x0A)
|
||||
return(false);
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
static INLINE uint8 BCD_to_U8(uint8 bcd_number)
|
||||
{
|
||||
return( ((bcd_number >> 4) * 10) + (bcd_number & 0x0F) );
|
||||
}
|
||||
|
||||
static INLINE uint8 U8_to_BCD(uint8 num)
|
||||
{
|
||||
return( ((num / 10) << 4) + (num % 10) );
|
||||
}
|
||||
|
||||
// should always perform the conversion, even if the bcd number is invalid.
|
||||
static INLINE bool BCD_to_U8_check(uint8 bcd_number, uint8 *out_number)
|
||||
{
|
||||
*out_number = BCD_to_U8(bcd_number);
|
||||
|
||||
if(!BCD_is_valid(bcd_number))
|
||||
return(false);
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
//
|
||||
// Sector data encoding functions(to full 2352 bytes raw sector).
|
||||
//
|
||||
// sector_data must be able to contain at least 2352 bytes.
|
||||
void encode_mode0_sector(uint32 aba, uint8 *sector_data);
|
||||
void encode_mode1_sector(uint32 aba, uint8 *sector_data); // 2048 bytes of user data at offset 16
|
||||
void encode_mode2_sector(uint32 aba, uint8 *sector_data); // 2336 bytes of user data at offset 16
|
||||
void encode_mode2_form1_sector(uint32 aba, uint8 *sector_data); // 2048+8 bytes of user data at offset 16
|
||||
void encode_mode2_form2_sector(uint32 aba, uint8 *sector_data); // 2324+8 bytes of user data at offset 16
|
||||
|
||||
|
||||
// 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 mode, const TOC& toc, const int32 lba, int32 lba_subq_relative_offs, uint8* out_buf);
|
||||
void subpw_synth_udapp_lba(const TOC& toc, const int32 lba, const int32 lba_subq_relative_offs, uint8* SubPWBuf);
|
||||
|
||||
// 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 mode, const TOC& toc, const int32 lba, uint8* out_buf);
|
||||
void subpw_synth_leadout_lba(const TOC& toc, const int32 lba, uint8* SubPWBuf);
|
||||
|
||||
|
||||
//
|
||||
// User data error detection and correction
|
||||
//
|
||||
|
||||
// Check EDC of a mode 1 or mode 2 form 1 sector.
|
||||
// Returns "true" if checksum is ok(matches).
|
||||
// Returns "false" if checksum mismatch.
|
||||
// sector_data should contain 2352 bytes of raw sector data.
|
||||
bool edc_check(const uint8 *sector_data, bool xa);
|
||||
|
||||
// Check EDC and L-EC data of a mode 1 or mode 2 form 1 sector, and correct bit errors if any exist.
|
||||
// Returns "true" if errors weren't detected, or they were corrected succesfully.
|
||||
// Returns "false" if errors couldn't be corrected.
|
||||
// sector_data should contain 2352 bytes of raw sector data.
|
||||
bool edc_lec_check_and_correct(uint8 *sector_data, bool xa);
|
||||
|
||||
//
|
||||
// Subchannel(Q in particular) functions
|
||||
//
|
||||
|
||||
// Returns false on checksum mismatch, true on match.
|
||||
bool subq_check_checksum(const uint8 *subq_buf);
|
||||
|
||||
// Calculates the checksum of Q subchannel data(not including the checksum bytes of course ;)) from subq_buf, and stores it into the appropriate position
|
||||
// in subq_buf.
|
||||
void subq_generate_checksum(uint8 *subq_buf);
|
||||
|
||||
// Deinterleaves 12 bytes of subchannel Q data from 96 bytes of interleaved subchannel PW data.
|
||||
void subq_deinterleave(const uint8 *subpw_buf, uint8 *subq_buf);
|
||||
|
||||
// Deinterleaves 96 bytes of subchannel P-W data from 96 bytes of interleaved subchannel PW data.
|
||||
void subpw_deinterleave(const uint8 *in_buf, uint8 *out_buf);
|
||||
|
||||
// Interleaves 96 bytes of subchannel P-W data from 96 bytes of uninterleaved subchannel PW data.
|
||||
void subpw_interleave(const uint8 *in_buf, uint8 *out_buf);
|
||||
|
||||
// Extrapolates Q subchannel current position data from subq_input, with frame/sector delta position_delta, and writes to subq_output.
|
||||
// Only valid for ADR_CURPOS.
|
||||
// subq_input must pass subq_check_checksum().
|
||||
// TODO
|
||||
//void subq_extrapolate(const uint8 *subq_input, int32 position_delta, uint8 *subq_output);
|
||||
|
||||
// (De)Scrambles data sector.
|
||||
void scrambleize_data_sector(uint8 *sector_data);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
mednafen_SOURCES += cdrom/audioreader.cpp cdrom/cdromif.cpp cdrom/scsicd.cpp
|
||||
mednafen_SOURCES += cdrom/CDUtility.cpp cdrom/crc32.cpp cdrom/galois.cpp cdrom/l-ec.cpp cdrom/recover-raw.cpp
|
||||
mednafen_SOURCES += cdrom/lec.cpp cdrom/CDAccess.cpp cdrom/CDAccess_Image.cpp cdrom/CDAccess_CCD.cpp
|
||||
|
||||
if HAVE_LIBCDIO
|
||||
mednafen_SOURCES += cdrom/CDAccess_Physical.cpp
|
||||
endif
|
||||
mednafen_SOURCES += cdrom/cdromif.cpp cdrom/scsicd.cpp
|
||||
mednafen_SOURCES += cdrom/CDUtility.cpp cdrom/crc32.cpp cdrom/galois.cpp cdrom/l-ec.cpp cdrom/recover-raw.cpp
|
||||
mednafen_SOURCES += cdrom/lec.cpp cdrom/CDAccess.cpp cdrom/CDAccess_Image.cpp cdrom/CDAccess_CCD.cpp
|
||||
|
||||
mednafen_SOURCES += cdrom/CDAFReader.cpp
|
||||
mednafen_SOURCES += cdrom/CDAFReader_Vorbis.cpp
|
||||
mednafen_SOURCES += cdrom/CDAFReader_MPC.cpp
|
||||
|
||||
if HAVE_LIBSNDFILE
|
||||
mednafen_SOURCES += cdrom/CDAFReader_SF.cpp
|
||||
endif
|
||||
|
|
|
@ -1,617 +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 "emuware/emuware.h"
|
||||
#include "audioreader.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifdef HAVE_AUDIOREADER
|
||||
#include "../tremor/ivorbisfile.h"
|
||||
#include "../mpcdec/mpcdec.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LIBSNDFILE
|
||||
#include <sndfile.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_OPUSFILE
|
||||
#include "audioreader_opus.h"
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "../general.h"
|
||||
#include "../endian.h"
|
||||
|
||||
AudioReader::AudioReader() : LastReadPos(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
AudioReader::~AudioReader()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
int64 AudioReader::Read_(int16 *buffer, int64 frames)
|
||||
{
|
||||
abort();
|
||||
return(false);
|
||||
}
|
||||
|
||||
bool AudioReader::Seek_(int64 frame_offset)
|
||||
{
|
||||
abort();
|
||||
return(false);
|
||||
}
|
||||
|
||||
int64 AudioReader::FrameCount(void)
|
||||
{
|
||||
abort();
|
||||
return(0);
|
||||
}
|
||||
|
||||
/*
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
*/
|
||||
|
||||
#ifdef HAVE_AUDIOREADER
|
||||
|
||||
class OggVorbisReader : public AudioReader
|
||||
{
|
||||
public:
|
||||
OggVorbisReader(Stream *fp);
|
||||
~OggVorbisReader();
|
||||
|
||||
int64 Read_(int16 *buffer, int64 frames);
|
||||
bool Seek_(int64 frame_offset);
|
||||
int64 FrameCount(void);
|
||||
|
||||
private:
|
||||
OggVorbis_File ovfile;
|
||||
Stream *fw;
|
||||
};
|
||||
|
||||
|
||||
static size_t iov_read_func(void *ptr, size_t size, size_t nmemb, void *user_data)
|
||||
{
|
||||
Stream *fw = (Stream*)user_data;
|
||||
|
||||
if(!size)
|
||||
return(0);
|
||||
|
||||
try
|
||||
{
|
||||
return fw->read(ptr, size * nmemb, false) / size;
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
}
|
||||
|
||||
static int iov_seek_func(void *user_data, ogg_int64_t offset, int whence)
|
||||
{
|
||||
Stream *fw = (Stream*)user_data;
|
||||
|
||||
try
|
||||
{
|
||||
fw->seek(offset, whence);
|
||||
return(0);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return(-1);
|
||||
}
|
||||
}
|
||||
|
||||
static int iov_close_func(void *user_data)
|
||||
{
|
||||
Stream *fw = (Stream*)user_data;
|
||||
|
||||
try
|
||||
{
|
||||
fw->close();
|
||||
return(0);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return EOF;
|
||||
}
|
||||
}
|
||||
|
||||
static long iov_tell_func(void *user_data)
|
||||
{
|
||||
Stream *fw = (Stream*)user_data;
|
||||
|
||||
try
|
||||
{
|
||||
return fw->tell();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return(-1);
|
||||
}
|
||||
}
|
||||
|
||||
OggVorbisReader::OggVorbisReader(Stream *fp) : fw(fp)
|
||||
{
|
||||
ov_callbacks cb;
|
||||
|
||||
memset(&cb, 0, sizeof(cb));
|
||||
cb.read_func = iov_read_func;
|
||||
cb.seek_func = iov_seek_func;
|
||||
cb.close_func = iov_close_func;
|
||||
cb.tell_func = iov_tell_func;
|
||||
|
||||
fp->seek(0, SEEK_SET);
|
||||
if(ov_open_callbacks(fp, &ovfile, NULL, 0, cb))
|
||||
throw(0);
|
||||
}
|
||||
|
||||
OggVorbisReader::~OggVorbisReader()
|
||||
{
|
||||
ov_clear(&ovfile);
|
||||
}
|
||||
|
||||
int64 OggVorbisReader::Read_(int16 *buffer, int64 frames)
|
||||
{
|
||||
uint8 *tw_buf = (uint8 *)buffer;
|
||||
int cursection = 0;
|
||||
long toread = frames * sizeof(int16) * 2;
|
||||
|
||||
while(toread > 0)
|
||||
{
|
||||
long didread = ov_read(&ovfile, (char*)tw_buf, toread, &cursection);
|
||||
|
||||
if(didread == 0)
|
||||
break;
|
||||
|
||||
tw_buf = (uint8 *)tw_buf + didread;
|
||||
toread -= didread;
|
||||
}
|
||||
|
||||
return(frames - toread / sizeof(int16) / 2);
|
||||
}
|
||||
|
||||
bool OggVorbisReader::Seek_(int64 frame_offset)
|
||||
{
|
||||
ov_pcm_seek(&ovfile, frame_offset);
|
||||
return(true);
|
||||
}
|
||||
|
||||
int64 OggVorbisReader::FrameCount(void)
|
||||
{
|
||||
return(ov_pcm_total(&ovfile, -1));
|
||||
}
|
||||
|
||||
class MPCReader : public AudioReader
|
||||
{
|
||||
public:
|
||||
MPCReader(Stream *fp);
|
||||
~MPCReader();
|
||||
|
||||
int64 Read_(int16 *buffer, int64 frames);
|
||||
bool Seek_(int64 frame_offset);
|
||||
int64 FrameCount(void);
|
||||
|
||||
private:
|
||||
mpc_reader reader;
|
||||
mpc_demux *demux;
|
||||
mpc_streaminfo si;
|
||||
|
||||
MPC_SAMPLE_FORMAT MPCBuffer[MPC_DECODER_BUFFER_LENGTH];
|
||||
|
||||
uint32 MPCBufferIn;
|
||||
uint32 MPCBufferOffs;
|
||||
Stream *fw;
|
||||
};
|
||||
|
||||
|
||||
/// Reads size bytes of data into buffer at ptr.
|
||||
static mpc_int32_t impc_read(mpc_reader *p_reader, void *ptr, mpc_int32_t size)
|
||||
{
|
||||
Stream *fw = (Stream*)(p_reader->data);
|
||||
|
||||
try
|
||||
{
|
||||
return fw->read(ptr, size, false);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return(MPC_STATUS_FAIL);
|
||||
}
|
||||
}
|
||||
|
||||
/// Seeks to byte position offset.
|
||||
static mpc_bool_t impc_seek(mpc_reader *p_reader, mpc_int32_t offset)
|
||||
{
|
||||
Stream *fw = (Stream*)(p_reader->data);
|
||||
|
||||
try
|
||||
{
|
||||
fw->seek(offset, SEEK_SET);
|
||||
return(MPC_TRUE);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return(MPC_FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the current byte offset in the stream.
|
||||
static mpc_int32_t impc_tell(mpc_reader *p_reader)
|
||||
{
|
||||
Stream *fw = (Stream*)(p_reader->data);
|
||||
|
||||
try
|
||||
{
|
||||
return fw->tell();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return(MPC_STATUS_FAIL);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the total length of the source stream, in bytes.
|
||||
static mpc_int32_t impc_get_size(mpc_reader *p_reader)
|
||||
{
|
||||
Stream *fw = (Stream*)(p_reader->data);
|
||||
|
||||
try
|
||||
{
|
||||
return fw->size();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return(MPC_STATUS_FAIL);
|
||||
}
|
||||
}
|
||||
|
||||
/// True if the stream is a seekable stream.
|
||||
static mpc_bool_t impc_canseek(mpc_reader *p_reader)
|
||||
{
|
||||
return(MPC_TRUE);
|
||||
}
|
||||
|
||||
MPCReader::MPCReader(Stream *fp) : fw(fp)
|
||||
{
|
||||
fp->seek(0, SEEK_SET);
|
||||
|
||||
demux = NULL;
|
||||
memset(&si, 0, sizeof(si));
|
||||
memset(MPCBuffer, 0, sizeof(MPCBuffer));
|
||||
MPCBufferOffs = 0;
|
||||
MPCBufferIn = 0;
|
||||
|
||||
memset(&reader, 0, sizeof(reader));
|
||||
reader.read = impc_read;
|
||||
reader.seek = impc_seek;
|
||||
reader.tell = impc_tell;
|
||||
reader.get_size = impc_get_size;
|
||||
reader.canseek = impc_canseek;
|
||||
reader.data = (void*)fp;
|
||||
|
||||
if(!(demux = mpc_demux_init(&reader)))
|
||||
{
|
||||
throw(0);
|
||||
}
|
||||
mpc_demux_get_info(demux, &si);
|
||||
|
||||
if(si.channels != 2)
|
||||
{
|
||||
mpc_demux_exit(demux);
|
||||
demux = NULL;
|
||||
throw MDFN_Error(0, _("MusePack stream has wrong number of channels(%u); the correct number is 2."), si.channels);
|
||||
}
|
||||
|
||||
if(si.sample_freq != 44100)
|
||||
{
|
||||
mpc_demux_exit(demux);
|
||||
demux = NULL;
|
||||
throw MDFN_Error(0, _("MusePack stream has wrong samplerate(%u Hz); the correct samplerate is 44100 Hz."), si.sample_freq);
|
||||
}
|
||||
}
|
||||
|
||||
MPCReader::~MPCReader()
|
||||
{
|
||||
if(demux)
|
||||
{
|
||||
mpc_demux_exit(demux);
|
||||
demux = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int64 MPCReader::Read_(int16 *buffer, int64 frames)
|
||||
{
|
||||
mpc_status err;
|
||||
int16 *cowbuf = (int16 *)buffer;
|
||||
int32 toread = frames * 2;
|
||||
|
||||
while(toread > 0)
|
||||
{
|
||||
int32 tmplen;
|
||||
|
||||
if(!MPCBufferIn)
|
||||
{
|
||||
mpc_frame_info fi;
|
||||
memset(&fi, 0, sizeof(fi));
|
||||
|
||||
fi.buffer = MPCBuffer;
|
||||
if((err = mpc_demux_decode(demux, &fi)) < 0 || fi.bits == -1)
|
||||
return(frames - toread / 2);
|
||||
|
||||
MPCBufferIn = fi.samples * 2;
|
||||
MPCBufferOffs = 0;
|
||||
}
|
||||
|
||||
tmplen = MPCBufferIn;
|
||||
|
||||
if(tmplen >= toread)
|
||||
tmplen = toread;
|
||||
|
||||
for(int x = 0; x < tmplen; x++)
|
||||
{
|
||||
#ifdef MPC_FIXED_POINT
|
||||
int32 samp = MPCBuffer[MPCBufferOffs + x] >> MPC_FIXED_POINT_FRACTPART;
|
||||
#else
|
||||
#warning Floating-point MPC decoding path not tested.
|
||||
int32 samp = (int32)(MPCBuffer[MPCBufferOffs + x] * 32767);
|
||||
#endif
|
||||
if(samp < -32768)
|
||||
samp = -32768;
|
||||
|
||||
if(samp > 32767)
|
||||
samp = 32767;
|
||||
|
||||
*cowbuf = (int16)samp;
|
||||
cowbuf++;
|
||||
}
|
||||
|
||||
MPCBufferOffs += tmplen;
|
||||
toread -= tmplen;
|
||||
MPCBufferIn -= tmplen;
|
||||
}
|
||||
|
||||
return(frames - toread / 2);
|
||||
}
|
||||
|
||||
bool MPCReader::Seek_(int64 frame_offset)
|
||||
{
|
||||
MPCBufferOffs = 0;
|
||||
MPCBufferIn = 0;
|
||||
|
||||
if(mpc_demux_seek_sample(demux, frame_offset) < 0)
|
||||
return(false);
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
int64 MPCReader::FrameCount(void)
|
||||
{
|
||||
return(mpc_streaminfo_get_length_samples(&si));
|
||||
}
|
||||
|
||||
#endif //HAVE_AUDIOREADER
|
||||
/*
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
*/
|
||||
|
||||
#ifdef HAVE_LIBSNDFILE
|
||||
class SFReader : public AudioReader
|
||||
{
|
||||
public:
|
||||
|
||||
SFReader(Stream *fp);
|
||||
~SFReader();
|
||||
|
||||
int64 Read_(int16 *buffer, int64 frames);
|
||||
bool Seek_(int64 frame_offset);
|
||||
int64 FrameCount(void);
|
||||
|
||||
private:
|
||||
SNDFILE *sf;
|
||||
SF_INFO sfinfo;
|
||||
SF_VIRTUAL_IO sfvf;
|
||||
|
||||
Stream *fw;
|
||||
};
|
||||
|
||||
static sf_count_t isf_get_filelen(void *user_data)
|
||||
{
|
||||
Stream *fw = (Stream*)user_data;
|
||||
|
||||
try
|
||||
{
|
||||
return fw->size();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return(-1);
|
||||
}
|
||||
}
|
||||
|
||||
static sf_count_t isf_seek(sf_count_t offset, int whence, void *user_data)
|
||||
{
|
||||
Stream *fw = (Stream*)user_data;
|
||||
|
||||
try
|
||||
{
|
||||
//printf("Seek: offset=%lld, whence=%lld\n", (long long)offset, (long long)whence);
|
||||
|
||||
fw->seek(offset, whence);
|
||||
return fw->tell();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
//printf(" SEEK FAILED\n");
|
||||
return(-1);
|
||||
}
|
||||
}
|
||||
|
||||
static sf_count_t isf_read(void *ptr, sf_count_t count, void *user_data)
|
||||
{
|
||||
Stream *fw = (Stream*)user_data;
|
||||
|
||||
try
|
||||
{
|
||||
sf_count_t ret = fw->read(ptr, count, false);
|
||||
|
||||
//printf("Read: count=%lld, ret=%lld\n", (long long)count, (long long)ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
//printf(" READ FAILED\n");
|
||||
return(0);
|
||||
}
|
||||
}
|
||||
|
||||
static sf_count_t isf_write(const void *ptr, sf_count_t count, void *user_data)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
static sf_count_t isf_tell(void *user_data)
|
||||
{
|
||||
Stream *fw = (Stream*)user_data;
|
||||
|
||||
try
|
||||
{
|
||||
return fw->tell();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return(-1);
|
||||
}
|
||||
}
|
||||
|
||||
SFReader::SFReader(Stream *fp) : fw(fp)
|
||||
{
|
||||
fp->seek(0, SEEK_SET);
|
||||
|
||||
memset(&sfvf, 0, sizeof(sfvf));
|
||||
sfvf.get_filelen = isf_get_filelen;
|
||||
sfvf.seek = isf_seek;
|
||||
sfvf.read = isf_read;
|
||||
sfvf.write = isf_write;
|
||||
sfvf.tell = isf_tell;
|
||||
|
||||
memset(&sfinfo, 0, sizeof(sfinfo));
|
||||
if(!(sf = sf_open_virtual(&sfvf, SFM_READ, &sfinfo, (void*)fp)))
|
||||
throw(0);
|
||||
}
|
||||
|
||||
SFReader::~SFReader()
|
||||
{
|
||||
sf_close(sf);
|
||||
}
|
||||
|
||||
int64 SFReader::Read_(int16 *buffer, int64 frames)
|
||||
{
|
||||
return(sf_read_short(sf, (short*)buffer, frames * 2) / 2);
|
||||
}
|
||||
|
||||
bool SFReader::Seek_(int64 frame_offset)
|
||||
{
|
||||
// FIXME error condition
|
||||
if(sf_seek(sf, frame_offset, SEEK_SET) != frame_offset)
|
||||
return(false);
|
||||
return(true);
|
||||
}
|
||||
|
||||
int64 SFReader::FrameCount(void)
|
||||
{
|
||||
return(sfinfo.frames);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
AudioReader *AR_Open(Stream *fp)
|
||||
{
|
||||
#ifdef HAVE_AUDIOREADER
|
||||
try
|
||||
{
|
||||
return new MPCReader(fp);
|
||||
}
|
||||
catch(int i)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_OPUSFILE
|
||||
try
|
||||
{
|
||||
return new OpusReader(fp);
|
||||
}
|
||||
catch(int i)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_AUDIOREADER
|
||||
try
|
||||
{
|
||||
return new OggVorbisReader(fp);
|
||||
}
|
||||
catch(int i)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LIBSNDFILE
|
||||
try
|
||||
{
|
||||
return new SFReader(fp);
|
||||
}
|
||||
catch(int i)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
return(NULL);
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
#ifndef __MDFN_AUDIOREADER_H
|
||||
#define __MDFN_AUDIOREADER_H
|
||||
|
||||
#include "../Stream.h"
|
||||
|
||||
class AudioReader
|
||||
{
|
||||
public:
|
||||
AudioReader();
|
||||
virtual ~AudioReader();
|
||||
|
||||
virtual int64 FrameCount(void);
|
||||
INLINE int64 Read(int64 frame_offset, int16 *buffer, int64 frames)
|
||||
{
|
||||
int64 ret;
|
||||
|
||||
//if(frame_offset >= 0)
|
||||
{
|
||||
if(LastReadPos != frame_offset)
|
||||
{
|
||||
//puts("SEEK");
|
||||
if(!Seek_(frame_offset))
|
||||
return(0);
|
||||
LastReadPos = frame_offset;
|
||||
}
|
||||
}
|
||||
ret = Read_(buffer, frames);
|
||||
LastReadPos += ret;
|
||||
return(ret);
|
||||
}
|
||||
|
||||
private:
|
||||
virtual int64 Read_(int16 *buffer, int64 frames);
|
||||
virtual bool Seek_(int64 frame_offset);
|
||||
|
||||
int64 LastReadPos;
|
||||
};
|
||||
|
||||
// AR_Open(), and AudioReader, will NOT take "ownership" of the Stream object(IE it won't ever delete it). Though it does assume it has exclusive access
|
||||
// to it for as long as the AudioReader object exists.
|
||||
AudioReader *AR_Open(Stream *fp);
|
||||
|
||||
#endif
|
|
@ -1,185 +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
|
||||
*/
|
||||
|
||||
#include "../mednafen.h"
|
||||
#include "audioreader.h"
|
||||
#include "audioreader_opus.h"
|
||||
|
||||
// OPUS SUPPORT NOT DONE YET!!!
|
||||
/*
|
||||
|
||||
(int64)op_pcm_total() * 44100 / 48000
|
||||
|
||||
resampling vs seek, filter delay, etc. to consider
|
||||
*/
|
||||
|
||||
static size_t iop_read_func(void *ptr, size_t size, size_t nmemb, void *user_data)
|
||||
{
|
||||
Stream *fw = (Stream*)user_data;
|
||||
|
||||
if(!size)
|
||||
return(0);
|
||||
|
||||
try
|
||||
{
|
||||
return fw->read(ptr, size * nmemb, false) / size;
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
}
|
||||
|
||||
static int iop_seek_func(void *user_data, opus_int64 offset, int whence)
|
||||
{
|
||||
Stream *fw = (Stream*)user_data;
|
||||
|
||||
try
|
||||
{
|
||||
fw->seek(offset, whence);
|
||||
return(0);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return(-1);
|
||||
}
|
||||
}
|
||||
|
||||
static int iop_close_func(void *user_data)
|
||||
{
|
||||
Stream *fw = (Stream*)user_data;
|
||||
|
||||
try
|
||||
{
|
||||
fw->close();
|
||||
return(0);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return EOF;
|
||||
}
|
||||
}
|
||||
|
||||
static opus_int64 iop_tell_func(void *user_data)
|
||||
{
|
||||
Stream *fw = (Stream*)user_data;
|
||||
|
||||
try
|
||||
{
|
||||
return fw->tell();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return(-1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Error strings copied from libopusfile header file comments. */
|
||||
static const char *op_errstring(int error)
|
||||
{
|
||||
static const struct
|
||||
{
|
||||
int code;
|
||||
const char *str;
|
||||
} error_table[] =
|
||||
{
|
||||
{ OP_EREAD, gettext_noop("OP_EREAD: An underlying read, seek, or tell operation failed when it should have succeeded.") },
|
||||
{ OP_EFAULT, gettext_noop("OP_EFAULT: A NULL pointer was passed where one was unexpected, or an internal memory allocation failed, or an internal library error was encountered.") },
|
||||
{ OP_EIMPL, gettext_noop("OP_EIMPL: The stream used a feature that is not implemented, such as an unsupported channel family.") },
|
||||
{ OP_EINVAL, gettext_noop("OP_EINVAL: One or more parameters to a function were invalid.") },
|
||||
{ OP_ENOTFORMAT, gettext_noop("OP_ENOTFORMAT: A purported Ogg Opus stream did not begin with an Ogg page, or a purported header packet did not start with one of the required strings, \"OpusHead\" or \"OpusTags\".") },
|
||||
{ OP_EBADHEADER, gettext_noop("OP_EBADHEADER: A required header packet was not properly formatted, contained illegal values, or was missing altogether.") },
|
||||
{ OP_EVERSION, gettext_noop("OP_EVERSION: The ID header contained an unrecognized version number.") },
|
||||
{ OP_EBADPACKET, gettext_noop("OP_EBADPACKET: An audio packet failed to decode properly.") },
|
||||
{ OP_EBADLINK, gettext_noop("OP_EBADLINK: We failed to find data we had seen before, or the bitstream structure was sufficiently malformed that seeking to the target destination was impossible.") },
|
||||
{ OP_ENOSEEK, gettext_noop("OP_ENOSEEK: An operation that requires seeking was requested on an unseekable stream.") },
|
||||
{ OP_EBADTIMESTAMP, gettext_noop("OP_EBADTIMESTAMP: The first or last granule position of a link failed basic validity checks.") },
|
||||
};
|
||||
|
||||
for(unsigned i = 0; i < sizeof(error_table) / sizeof(error_table[0]); i++)
|
||||
{
|
||||
if(error_table[i].code == error)
|
||||
{
|
||||
return _(error_table[i].str);
|
||||
}
|
||||
}
|
||||
|
||||
return _("Unknown");
|
||||
}
|
||||
|
||||
OggOpusReader::OggOpusReader(Stream *fp) : fw(fp)
|
||||
{
|
||||
OpusFileCallbacks cb;
|
||||
int error = 0;
|
||||
|
||||
memset(&cb, 0, sizeof(cb));
|
||||
cb.read_func = iop_read_func;
|
||||
cb.seek_func = iop_seek_func;
|
||||
cb.close_func = iop_close_func;
|
||||
cb.tell_func = iop_tell_func;
|
||||
|
||||
fp->seek(0, SEEK_SET);
|
||||
|
||||
if(!(opfile = op_open_callbacks((void*)fp, &cb, NULL, 0, &error)))
|
||||
{
|
||||
switch(error)
|
||||
{
|
||||
default:
|
||||
throw MDFN_Error(0, _("opusfile: error code: %d(%s)", error, op_errstring(error)));
|
||||
break;
|
||||
|
||||
case OP_ENOTFORMAT:
|
||||
throw(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OggOpusReader::~OggOpusReader()
|
||||
{
|
||||
op_free(opfile);
|
||||
}
|
||||
|
||||
int64 OggOpusReader::Read_(int16 *buffer, int64 frames)
|
||||
{
|
||||
int16 *tr_buffer = buffer;
|
||||
int64 tr_count = frames * 2;
|
||||
|
||||
while(tr_count > 0)
|
||||
{
|
||||
int64 didread = op_read(opfile, tr_buffer, tr_count, NULL);
|
||||
|
||||
if(didread == 0)
|
||||
break;
|
||||
|
||||
tr_buffer += didread * 2;
|
||||
tr_count -= didread * 2;
|
||||
}
|
||||
|
||||
return(frames - (tr_count / 2));
|
||||
}
|
||||
|
||||
bool OggOpusReader::Seek_(int64 frame_offset)
|
||||
{
|
||||
op_pcm_seek(opfile, frame_offset);
|
||||
return(true);
|
||||
}
|
||||
|
||||
int64 OggOpusReader::FrameCount(void)
|
||||
{
|
||||
return(op_pcm_total(pvfile, -1));
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
#ifndef __MDFN_AUDIOREADER_OPUS_H
|
||||
#define __MDFN_AUDIOREADER_OPUS_H
|
||||
|
||||
#include <opus/opusfile.h>
|
||||
|
||||
class OggOpusReader : public AudioReader
|
||||
{
|
||||
public:
|
||||
OggOpusReader(Stream *fp);
|
||||
~OggOpusReader();
|
||||
|
||||
int64 Read_(int16 *buffer, int64 frames);
|
||||
bool Seek_(int64 frame_offset);
|
||||
int64 FrameCount(void);
|
||||
|
||||
private:
|
||||
OggOpus_File *opfile;
|
||||
Stream *fw;
|
||||
};
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -1,70 +1,66 @@
|
|||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef __MDFN_CDROM_CDROMIF_H
|
||||
#define __MDFN_CDROM_CDROMIF_H
|
||||
|
||||
#include "CDUtility.h"
|
||||
#include "stream.h"
|
||||
|
||||
#include <queue>
|
||||
|
||||
typedef CDUtility::TOC CD_TOC;
|
||||
|
||||
class CDIF
|
||||
{
|
||||
public:
|
||||
|
||||
CDIF();
|
||||
virtual ~CDIF();
|
||||
|
||||
inline void ReadTOC(CDUtility::TOC *read_target)
|
||||
{
|
||||
*read_target = disc_toc;
|
||||
}
|
||||
|
||||
virtual void HintReadSector(uint32 lba) = 0;
|
||||
virtual bool ReadRawSector(uint8 *buf, uint32 lba) = 0;
|
||||
|
||||
// Call for mode 1 or mode 2 form 1 only.
|
||||
bool ValidateRawSector(uint8 *buf);
|
||||
|
||||
// Utility/Wrapped functions
|
||||
// Reads mode 1 and mode2 form 1 sectors(2048 bytes per sector returned)
|
||||
// Will return the type(1, 2) of the first sector read to the buffer supplied, 0 on error
|
||||
int ReadSector(uint8* pBuf, uint32 lba, uint32 nSectors);
|
||||
|
||||
// Return true if operation succeeded or it was a NOP(either due to not being implemented, or the current status matches eject_status).
|
||||
// Returns false on failure(usually drive error of some kind; not completely fatal, can try again).
|
||||
virtual bool Eject(bool eject_status) = 0;
|
||||
|
||||
inline bool IsPhysical(void) { return(is_phys_cache); }
|
||||
|
||||
// For Mode 1, or Mode 2 Form 1.
|
||||
// No reference counting or whatever is done, so if you destroy the CDIF object before you destroy the returned Stream, things will go BOOM.
|
||||
Stream *MakeStream(uint32 lba, uint32 sector_count);
|
||||
|
||||
protected:
|
||||
bool UnrecoverableError;
|
||||
bool is_phys_cache;
|
||||
CDUtility::TOC disc_toc;
|
||||
int DiscEjected; // 0 = inserted, 1 = ejected, -1 = DRAGONS ATE THE DISC. NOM NOM NOM.
|
||||
};
|
||||
|
||||
CDIF *CDIF_Open(const std::string& path, const bool is_device, bool image_memcache);
|
||||
|
||||
#endif
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef __MDFN_CDROM_CDROMIF_H
|
||||
#define __MDFN_CDROM_CDROMIF_H
|
||||
|
||||
#include "CDUtility.h"
|
||||
#include "stream.h"
|
||||
|
||||
#include <queue>
|
||||
|
||||
typedef CDUtility::TOC CD_TOC;
|
||||
|
||||
class CDIF
|
||||
{
|
||||
public:
|
||||
|
||||
CDIF();
|
||||
virtual ~CDIF();
|
||||
|
||||
static const int32 LBA_Read_Minimum = -150;
|
||||
static const int32 LBA_Read_Maximum = 449849; // 100 * 75 * 60 - 150 - 1
|
||||
|
||||
inline void ReadTOC(CDUtility::TOC *read_target)
|
||||
{
|
||||
*read_target = disc_toc;
|
||||
}
|
||||
|
||||
virtual void HintReadSector(int32 lba) = 0;
|
||||
virtual bool ReadRawSector(uint8 *buf, int32 lba) = 0; // Reads 2352+96 bytes of data into buf.
|
||||
virtual bool ReadRawSectorPWOnly(uint8* pwbuf, int32 lba, bool hint_fullread) = 0; // Reads 96 bytes(of raw subchannel PW data) into pwbuf.
|
||||
|
||||
// Call for mode 1 or mode 2 form 1 only.
|
||||
bool ValidateRawSector(uint8 *buf);
|
||||
|
||||
// Utility/Wrapped functions
|
||||
// Reads mode 1 and mode2 form 1 sectors(2048 bytes per sector returned)
|
||||
// Will return the type(1, 2) of the first sector read to the buffer supplied, 0 on error
|
||||
int ReadSector(uint8* buf, int32 lba, uint32 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(int32 lba, uint32 sector_count);
|
||||
|
||||
protected:
|
||||
bool UnrecoverableError;
|
||||
CDUtility::TOC disc_toc;
|
||||
};
|
||||
|
||||
CDIF *CDIF_Open(const std::string& path, bool image_memcache);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,203 +1,203 @@
|
|||
/* dvdisaster: Additional error correction for optical media.
|
||||
* Copyright (C) 2004-2007 Carsten Gnoerlich.
|
||||
* Project home page: http://www.dvdisaster.com
|
||||
* Email: carsten@dvdisaster.com -or- cgnoerlich@fsfe.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA,
|
||||
* or direct your browser at http://www.gnu.org.
|
||||
*/
|
||||
|
||||
#include "dvdisaster.h"
|
||||
|
||||
static GaloisTables *gt = NULL; /* for L-EC Reed-Solomon */
|
||||
static ReedSolomonTables *rt = NULL;
|
||||
|
||||
bool Init_LEC_Correct(void)
|
||||
{
|
||||
gt = CreateGaloisTables(0x11d);
|
||||
rt = CreateReedSolomonTables(gt, 0, 1, 10);
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
void Kill_LEC_Correct(void)
|
||||
{
|
||||
FreeGaloisTables(gt);
|
||||
FreeReedSolomonTables(rt);
|
||||
}
|
||||
|
||||
/***
|
||||
*** CD level CRC calculation
|
||||
***/
|
||||
|
||||
/*
|
||||
* Test raw sector against its 32bit CRC.
|
||||
* Returns TRUE if frame is good.
|
||||
*/
|
||||
|
||||
int CheckEDC(const unsigned char *cd_frame, bool xa_mode)
|
||||
{
|
||||
unsigned int expected_crc, real_crc;
|
||||
unsigned int crc_base = xa_mode ? 2072 : 2064;
|
||||
|
||||
expected_crc = cd_frame[crc_base + 0] << 0;
|
||||
expected_crc |= cd_frame[crc_base + 1] << 8;
|
||||
expected_crc |= cd_frame[crc_base + 2] << 16;
|
||||
expected_crc |= cd_frame[crc_base + 3] << 24;
|
||||
|
||||
if(xa_mode)
|
||||
real_crc = EDCCrc32(cd_frame+16, 2056);
|
||||
else
|
||||
real_crc = EDCCrc32(cd_frame, 2064);
|
||||
|
||||
if(expected_crc == real_crc)
|
||||
return(1);
|
||||
else
|
||||
{
|
||||
//printf("Bad EDC CRC: Calculated: %08x, Recorded: %08x\n", real_crc, expected_crc);
|
||||
return(0);
|
||||
}
|
||||
}
|
||||
|
||||
/***
|
||||
*** A very simple L-EC error correction.
|
||||
***
|
||||
* Perform just one pass over the Q and P vectors to see if everything
|
||||
* is okay respectively correct minor errors. This is pretty much the
|
||||
* same stuff the drive is supposed to do in the final L-EC stage.
|
||||
*/
|
||||
|
||||
static int simple_lec(unsigned char *frame)
|
||||
{
|
||||
unsigned char byte_state[2352];
|
||||
unsigned char p_vector[P_VECTOR_SIZE];
|
||||
unsigned char q_vector[Q_VECTOR_SIZE];
|
||||
unsigned char p_state[P_VECTOR_SIZE];
|
||||
int erasures[Q_VECTOR_SIZE], erasure_count;
|
||||
int ignore[2];
|
||||
int p_failures, q_failures;
|
||||
int p_corrected, q_corrected;
|
||||
int p,q;
|
||||
|
||||
/* Setup */
|
||||
|
||||
memset(byte_state, 0, 2352);
|
||||
|
||||
p_failures = q_failures = 0;
|
||||
p_corrected = q_corrected = 0;
|
||||
|
||||
/* Perform Q-Parity error correction */
|
||||
|
||||
for(q=0; q<N_Q_VECTORS; q++)
|
||||
{ int err;
|
||||
|
||||
/* We have no erasure information for Q vectors */
|
||||
|
||||
GetQVector(frame, q_vector, q);
|
||||
err = DecodePQ(rt, q_vector, Q_PADDING, ignore, 0);
|
||||
|
||||
/* See what we've got */
|
||||
|
||||
if(err < 0) /* Uncorrectable. Mark bytes are erasure. */
|
||||
{ q_failures++;
|
||||
FillQVector(byte_state, 1, q);
|
||||
}
|
||||
else /* Correctable */
|
||||
{ if(err == 1 || err == 2) /* Store back corrected vector */
|
||||
{ SetQVector(frame, q_vector, q);
|
||||
q_corrected++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Perform P-Parity error correction */
|
||||
|
||||
for(p=0; p<N_P_VECTORS; p++)
|
||||
{ int err,i;
|
||||
|
||||
/* Try error correction without erasure information */
|
||||
|
||||
GetPVector(frame, p_vector, p);
|
||||
err = DecodePQ(rt, p_vector, P_PADDING, ignore, 0);
|
||||
|
||||
/* If unsuccessful, try again using erasures.
|
||||
Erasure information is uncertain, so try this last. */
|
||||
|
||||
if(err < 0 || err > 2)
|
||||
{ GetPVector(byte_state, p_state, p);
|
||||
erasure_count = 0;
|
||||
|
||||
for(i=0; i<P_VECTOR_SIZE; i++)
|
||||
if(p_state[i])
|
||||
erasures[erasure_count++] = i;
|
||||
|
||||
if(erasure_count > 0 && erasure_count <= 2)
|
||||
{ GetPVector(frame, p_vector, p);
|
||||
err = DecodePQ(rt, p_vector, P_PADDING, erasures, erasure_count);
|
||||
}
|
||||
}
|
||||
|
||||
/* See what we've got */
|
||||
|
||||
if(err < 0) /* Uncorrectable. */
|
||||
{ p_failures++;
|
||||
}
|
||||
else /* Correctable. */
|
||||
{ if(err == 1 || err == 2) /* Store back corrected vector */
|
||||
{ SetPVector(frame, p_vector, p);
|
||||
p_corrected++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Sum up */
|
||||
|
||||
if(q_failures || p_failures || q_corrected || p_corrected)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***
|
||||
*** Validate CD raw sector
|
||||
***/
|
||||
|
||||
int ValidateRawSector(unsigned char *frame, bool xaMode)
|
||||
{
|
||||
int lec_did_sth = FALSE_0;
|
||||
|
||||
/* Do simple L-EC.
|
||||
It seems that drives stop their internal L-EC as soon as the
|
||||
EDC is okay, so we may see uncorrected errors in the parity bytes.
|
||||
Since we are also interested in the user data only and doing the
|
||||
L-EC is expensive, we skip our L-EC as well when the EDC is fine. */
|
||||
|
||||
if(!CheckEDC(frame, xaMode))
|
||||
{
|
||||
lec_did_sth = simple_lec(frame);
|
||||
}
|
||||
/* Test internal sector checksum again */
|
||||
|
||||
if(!CheckEDC(frame, xaMode))
|
||||
{
|
||||
/* EDC failure in RAW sector */
|
||||
return FALSE_0;
|
||||
}
|
||||
|
||||
return TRUE_1;
|
||||
}
|
||||
|
||||
/* dvdisaster: Additional error correction for optical media.
|
||||
* Copyright (C) 2004-2007 Carsten Gnoerlich.
|
||||
* Project home page: http://www.dvdisaster.com
|
||||
* Email: carsten@dvdisaster.com -or- cgnoerlich@fsfe.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA,
|
||||
* or direct your browser at http://www.gnu.org.
|
||||
*/
|
||||
|
||||
#include "dvdisaster.h"
|
||||
|
||||
static GaloisTables *gt = NULL; /* for L-EC Reed-Solomon */
|
||||
static ReedSolomonTables *rt = NULL;
|
||||
|
||||
bool Init_LEC_Correct(void)
|
||||
{
|
||||
gt = CreateGaloisTables(0x11d);
|
||||
rt = CreateReedSolomonTables(gt, 0, 1, 10);
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
void Kill_LEC_Correct(void)
|
||||
{
|
||||
FreeGaloisTables(gt);
|
||||
FreeReedSolomonTables(rt);
|
||||
}
|
||||
|
||||
/***
|
||||
*** CD level CRC calculation
|
||||
***/
|
||||
|
||||
/*
|
||||
* Test raw sector against its 32bit CRC.
|
||||
* Returns TRUE if frame is good.
|
||||
*/
|
||||
|
||||
int CheckEDC(const unsigned char *cd_frame, bool xa_mode)
|
||||
{
|
||||
unsigned int expected_crc, real_crc;
|
||||
unsigned int crc_base = xa_mode ? 2072 : 2064;
|
||||
|
||||
expected_crc = cd_frame[crc_base + 0] << 0;
|
||||
expected_crc |= cd_frame[crc_base + 1] << 8;
|
||||
expected_crc |= cd_frame[crc_base + 2] << 16;
|
||||
expected_crc |= cd_frame[crc_base + 3] << 24;
|
||||
|
||||
if(xa_mode)
|
||||
real_crc = EDCCrc32(cd_frame+16, 2056);
|
||||
else
|
||||
real_crc = EDCCrc32(cd_frame, 2064);
|
||||
|
||||
if(expected_crc == real_crc)
|
||||
return(1);
|
||||
else
|
||||
{
|
||||
//printf("Bad EDC CRC: Calculated: %08x, Recorded: %08x\n", real_crc, expected_crc);
|
||||
return(0);
|
||||
}
|
||||
}
|
||||
|
||||
/***
|
||||
*** A very simple L-EC error correction.
|
||||
***
|
||||
* Perform just one pass over the Q and P vectors to see if everything
|
||||
* is okay respectively correct minor errors. This is pretty much the
|
||||
* same stuff the drive is supposed to do in the final L-EC stage.
|
||||
*/
|
||||
|
||||
static int simple_lec(unsigned char *frame)
|
||||
{
|
||||
unsigned char byte_state[2352];
|
||||
unsigned char p_vector[P_VECTOR_SIZE];
|
||||
unsigned char q_vector[Q_VECTOR_SIZE];
|
||||
unsigned char p_state[P_VECTOR_SIZE];
|
||||
int erasures[Q_VECTOR_SIZE], erasure_count;
|
||||
int ignore[2];
|
||||
int p_failures, q_failures;
|
||||
int p_corrected, q_corrected;
|
||||
int p,q;
|
||||
|
||||
/* Setup */
|
||||
|
||||
memset(byte_state, 0, 2352);
|
||||
|
||||
p_failures = q_failures = 0;
|
||||
p_corrected = q_corrected = 0;
|
||||
|
||||
/* Perform Q-Parity error correction */
|
||||
|
||||
for(q=0; q<N_Q_VECTORS; q++)
|
||||
{ int err;
|
||||
|
||||
/* We have no erasure information for Q vectors */
|
||||
|
||||
GetQVector(frame, q_vector, q);
|
||||
err = DecodePQ(rt, q_vector, Q_PADDING, ignore, 0);
|
||||
|
||||
/* See what we've got */
|
||||
|
||||
if(err < 0) /* Uncorrectable. Mark bytes are erasure. */
|
||||
{ q_failures++;
|
||||
FillQVector(byte_state, 1, q);
|
||||
}
|
||||
else /* Correctable */
|
||||
{ if(err == 1 || err == 2) /* Store back corrected vector */
|
||||
{ SetQVector(frame, q_vector, q);
|
||||
q_corrected++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Perform P-Parity error correction */
|
||||
|
||||
for(p=0; p<N_P_VECTORS; p++)
|
||||
{ int err,i;
|
||||
|
||||
/* Try error correction without erasure information */
|
||||
|
||||
GetPVector(frame, p_vector, p);
|
||||
err = DecodePQ(rt, p_vector, P_PADDING, ignore, 0);
|
||||
|
||||
/* If unsuccessful, try again using erasures.
|
||||
Erasure information is uncertain, so try this last. */
|
||||
|
||||
if(err < 0 || err > 2)
|
||||
{ GetPVector(byte_state, p_state, p);
|
||||
erasure_count = 0;
|
||||
|
||||
for(i=0; i<P_VECTOR_SIZE; i++)
|
||||
if(p_state[i])
|
||||
erasures[erasure_count++] = i;
|
||||
|
||||
if(erasure_count > 0 && erasure_count <= 2)
|
||||
{ GetPVector(frame, p_vector, p);
|
||||
err = DecodePQ(rt, p_vector, P_PADDING, erasures, erasure_count);
|
||||
}
|
||||
}
|
||||
|
||||
/* See what we've got */
|
||||
|
||||
if(err < 0) /* Uncorrectable. */
|
||||
{ p_failures++;
|
||||
}
|
||||
else /* Correctable. */
|
||||
{ if(err == 1 || err == 2) /* Store back corrected vector */
|
||||
{ SetPVector(frame, p_vector, p);
|
||||
p_corrected++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Sum up */
|
||||
|
||||
if(q_failures || p_failures || q_corrected || p_corrected)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***
|
||||
*** Validate CD raw sector
|
||||
***/
|
||||
|
||||
int ValidateRawSector(unsigned char *frame, bool xaMode)
|
||||
{
|
||||
int lec_did_sth = FALSE_0;
|
||||
|
||||
/* Do simple L-EC.
|
||||
It seems that drives stop their internal L-EC as soon as the
|
||||
EDC is okay, so we may see uncorrected errors in the parity bytes.
|
||||
Since we are also interested in the user data only and doing the
|
||||
L-EC is expensive, we skip our L-EC as well when the EDC is fine. */
|
||||
|
||||
if(!CheckEDC(frame, xaMode))
|
||||
{
|
||||
lec_did_sth = simple_lec(frame);
|
||||
}
|
||||
/* Test internal sector checksum again */
|
||||
|
||||
if(!CheckEDC(frame, xaMode))
|
||||
{
|
||||
/* EDC failure in RAW sector */
|
||||
return FALSE_0;
|
||||
}
|
||||
|
||||
return TRUE_1;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,259 +1,259 @@
|
|||
/********************************************************
|
||||
* *
|
||||
* PC Engine CD Command 0xD8 - SAPSP *
|
||||
* *
|
||||
********************************************************/
|
||||
static void DoNEC_PCE_SAPSP(const uint8 *cdb)
|
||||
{
|
||||
uint32 new_read_sec_start;
|
||||
|
||||
//printf("Set audio start: %02x %02x %02x %02x %02x %02x %02x\n", cdb[9], cdb[1], cdb[2], cdb[3], cdb[4], cdb[5], cdb[6]);
|
||||
switch (cdb[9] & 0xc0)
|
||||
{
|
||||
default: SCSIDBG("Unknown SAPSP 9: %02x\n", cdb[9]);
|
||||
case 0x00:
|
||||
new_read_sec_start = (cdb[3] << 16) | (cdb[4] << 8) | cdb[5];
|
||||
break;
|
||||
|
||||
case 0x40:
|
||||
new_read_sec_start = AMSF_to_LBA(BCD_to_U8(cdb[2]), BCD_to_U8(cdb[3]), BCD_to_U8(cdb[4]));
|
||||
break;
|
||||
|
||||
case 0x80:
|
||||
{
|
||||
int track = BCD_to_U8(cdb[2]);
|
||||
|
||||
if(!track)
|
||||
track = 1;
|
||||
else if(track >= toc.last_track + 1)
|
||||
track = 100;
|
||||
new_read_sec_start = toc.tracks[track].lba;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
//printf("%lld\n", (long long)(monotonic_timestamp - pce_lastsapsp_timestamp) * 1000 / System_Clock);
|
||||
if(cdda.CDDAStatus == CDDASTATUS_PLAYING && new_read_sec_start == read_sec_start && ((int64)(monotonic_timestamp - pce_lastsapsp_timestamp) * 1000 / System_Clock) < 190)
|
||||
{
|
||||
pce_lastsapsp_timestamp = monotonic_timestamp;
|
||||
|
||||
SendStatusAndMessage(STATUS_GOOD, 0x00);
|
||||
CDIRQCallback(SCSICD_IRQ_DATA_TRANSFER_DONE);
|
||||
return;
|
||||
}
|
||||
|
||||
pce_lastsapsp_timestamp = monotonic_timestamp;
|
||||
|
||||
read_sec = read_sec_start = new_read_sec_start;
|
||||
read_sec_end = toc.tracks[100].lba;
|
||||
|
||||
|
||||
cdda.CDDAReadPos = 588;
|
||||
|
||||
cdda.CDDAStatus = CDDASTATUS_PAUSED;
|
||||
cdda.PlayMode = PLAYMODE_SILENT;
|
||||
|
||||
if(cdb[1])
|
||||
{
|
||||
cdda.PlayMode = PLAYMODE_NORMAL;
|
||||
cdda.CDDAStatus = CDDASTATUS_PLAYING;
|
||||
}
|
||||
|
||||
if(read_sec < toc.tracks[100].lba)
|
||||
Cur_CDIF->HintReadSector(read_sec);
|
||||
|
||||
SendStatusAndMessage(STATUS_GOOD, 0x00);
|
||||
CDIRQCallback(SCSICD_IRQ_DATA_TRANSFER_DONE);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/********************************************************
|
||||
* *
|
||||
* PC Engine CD Command 0xD9 - SAPEP *
|
||||
* *
|
||||
********************************************************/
|
||||
static void DoNEC_PCE_SAPEP(const uint8 *cdb)
|
||||
{
|
||||
uint32 new_read_sec_end;
|
||||
|
||||
//printf("Set audio end: %02x %02x %02x %02x %02x %02x %02x\n", cdb[9], cdb[1], cdb[2], cdb[3], cdb[4], cdb[5], cdb[6]);
|
||||
|
||||
switch (cdb[9] & 0xc0)
|
||||
{
|
||||
default: SCSIDBG("Unknown SAPEP 9: %02x\n", cdb[9]);
|
||||
|
||||
case 0x00:
|
||||
new_read_sec_end = (cdb[3] << 16) | (cdb[4] << 8) | cdb[5];
|
||||
break;
|
||||
|
||||
case 0x40:
|
||||
new_read_sec_end = BCD_to_U8(cdb[4]) + 75 * (BCD_to_U8(cdb[3]) + 60 * BCD_to_U8(cdb[2]));
|
||||
new_read_sec_end -= 150;
|
||||
break;
|
||||
|
||||
case 0x80:
|
||||
{
|
||||
int track = BCD_to_U8(cdb[2]);
|
||||
|
||||
if(!track)
|
||||
track = 1;
|
||||
else if(track >= toc.last_track + 1)
|
||||
track = 100;
|
||||
new_read_sec_end = toc.tracks[track].lba;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
read_sec_end = new_read_sec_end;
|
||||
|
||||
switch(cdb[1]) // PCE CD(TODO: Confirm these, and check the mode mask):
|
||||
{
|
||||
default:
|
||||
case 0x03: cdda.PlayMode = PLAYMODE_NORMAL;
|
||||
cdda.CDDAStatus = CDDASTATUS_PLAYING;
|
||||
break;
|
||||
|
||||
case 0x02: cdda.PlayMode = PLAYMODE_INTERRUPT;
|
||||
cdda.CDDAStatus = CDDASTATUS_PLAYING;
|
||||
break;
|
||||
|
||||
case 0x01: cdda.PlayMode = PLAYMODE_LOOP;
|
||||
cdda.CDDAStatus = CDDASTATUS_PLAYING;
|
||||
break;
|
||||
|
||||
case 0x00: cdda.PlayMode = PLAYMODE_SILENT;
|
||||
cdda.CDDAStatus = CDDASTATUS_STOPPED;
|
||||
break;
|
||||
}
|
||||
|
||||
SendStatusAndMessage(STATUS_GOOD, 0x00);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/********************************************************
|
||||
* *
|
||||
* PC Engine CD Command 0xDA - Pause *
|
||||
* *
|
||||
********************************************************/
|
||||
static void DoNEC_PCE_PAUSE(const uint8 *cdb)
|
||||
{
|
||||
if(cdda.CDDAStatus != CDDASTATUS_STOPPED) // Hmm, should we give an error if it tries to pause and it's already paused?
|
||||
{
|
||||
cdda.CDDAStatus = CDDASTATUS_PAUSED;
|
||||
SendStatusAndMessage(STATUS_GOOD, 0x00);
|
||||
}
|
||||
else // Definitely give an error if it tries to pause when no track is playing!
|
||||
{
|
||||
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_AUDIO_NOT_PLAYING);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/********************************************************
|
||||
* *
|
||||
* PC Engine CD Command 0xDD - Read Subchannel Q *
|
||||
* *
|
||||
********************************************************/
|
||||
static void DoNEC_PCE_READSUBQ(const uint8 *cdb)
|
||||
{
|
||||
uint8 *SubQBuf = cd.SubQBuf[QMode_Time];
|
||||
uint8 data_in[8192];
|
||||
|
||||
memset(data_in, 0x00, 10);
|
||||
|
||||
data_in[2] = SubQBuf[1]; // Track
|
||||
data_in[3] = SubQBuf[2]; // Index
|
||||
data_in[4] = SubQBuf[3]; // M(rel)
|
||||
data_in[5] = SubQBuf[4]; // S(rel)
|
||||
data_in[6] = SubQBuf[5]; // F(rel)
|
||||
data_in[7] = SubQBuf[7]; // M(abs)
|
||||
data_in[8] = SubQBuf[8]; // S(abs)
|
||||
data_in[9] = SubQBuf[9]; // F(abs)
|
||||
|
||||
if(cdda.CDDAStatus == CDDASTATUS_PAUSED)
|
||||
data_in[0] = 2; // Pause
|
||||
else if(cdda.CDDAStatus == CDDASTATUS_PLAYING || cdda.CDDAStatus == CDDASTATUS_SCANNING) // FIXME: Is this the correct status code for scanning playback?
|
||||
data_in[0] = 0; // Playing
|
||||
else
|
||||
data_in[0] = 3; // Stopped
|
||||
|
||||
DoSimpleDataIn(data_in, 10);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/********************************************************
|
||||
* *
|
||||
* PC Engine CD Command 0xDE - Get Directory Info *
|
||||
* *
|
||||
********************************************************/
|
||||
static void DoNEC_PCE_GETDIRINFO(const uint8 *cdb)
|
||||
{
|
||||
// Problems:
|
||||
// Returned data lengths on real PCE are not confirmed.
|
||||
// Mode 0x03 behavior not tested on real PCE
|
||||
|
||||
uint8 data_in[2048];
|
||||
uint32 data_in_size = 0;
|
||||
|
||||
memset(data_in, 0, sizeof(data_in));
|
||||
|
||||
switch(cdb[1])
|
||||
{
|
||||
default: MDFN_DispMessage("Unknown GETDIRINFO Mode: %02x", cdb[1]);
|
||||
printf("Unknown GETDIRINFO Mode: %02x", cdb[1]);
|
||||
case 0x0:
|
||||
data_in[0] = U8_to_BCD(toc.first_track);
|
||||
data_in[1] = U8_to_BCD(toc.last_track);
|
||||
|
||||
data_in_size = 2;
|
||||
break;
|
||||
|
||||
case 0x1:
|
||||
{
|
||||
uint8 m, s, f;
|
||||
|
||||
LBA_to_AMSF(toc.tracks[100].lba, &m, &s, &f);
|
||||
|
||||
data_in[0] = U8_to_BCD(m);
|
||||
data_in[1] = U8_to_BCD(s);
|
||||
data_in[2] = U8_to_BCD(f);
|
||||
|
||||
data_in_size = 3;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x2:
|
||||
{
|
||||
uint8 m, s, f;
|
||||
int track = BCD_to_U8(cdb[2]);
|
||||
|
||||
if(!track)
|
||||
track = 1;
|
||||
else if(cdb[2] == 0xAA)
|
||||
{
|
||||
track = 100;
|
||||
}
|
||||
else if(track > 99)
|
||||
{
|
||||
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
|
||||
return;
|
||||
}
|
||||
|
||||
LBA_to_AMSF(toc.tracks[track].lba, &m, &s, &f);
|
||||
|
||||
data_in[0] = U8_to_BCD(m);
|
||||
data_in[1] = U8_to_BCD(s);
|
||||
data_in[2] = U8_to_BCD(f);
|
||||
data_in[3] = toc.tracks[track].control;
|
||||
data_in_size = 4;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
DoSimpleDataIn(data_in, data_in_size);
|
||||
}
|
||||
|
||||
/********************************************************
|
||||
* *
|
||||
* PC Engine CD Command 0xD8 - SAPSP *
|
||||
* *
|
||||
********************************************************/
|
||||
static void DoNEC_PCE_SAPSP(const uint8 *cdb)
|
||||
{
|
||||
uint32 new_read_sec_start;
|
||||
|
||||
//printf("Set audio start: %02x %02x %02x %02x %02x %02x %02x\n", cdb[9], cdb[1], cdb[2], cdb[3], cdb[4], cdb[5], cdb[6]);
|
||||
switch (cdb[9] & 0xc0)
|
||||
{
|
||||
default: //SCSIDBG("Unknown SAPSP 9: %02x\n", cdb[9]);
|
||||
case 0x00:
|
||||
new_read_sec_start = (cdb[3] << 16) | (cdb[4] << 8) | cdb[5];
|
||||
break;
|
||||
|
||||
case 0x40:
|
||||
new_read_sec_start = AMSF_to_LBA(BCD_to_U8(cdb[2]), BCD_to_U8(cdb[3]), BCD_to_U8(cdb[4]));
|
||||
break;
|
||||
|
||||
case 0x80:
|
||||
{
|
||||
int track = BCD_to_U8(cdb[2]);
|
||||
|
||||
if(!track)
|
||||
track = 1;
|
||||
else if(track >= toc.last_track + 1)
|
||||
track = 100;
|
||||
new_read_sec_start = toc.tracks[track].lba;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
//printf("%lld\n", (long long)(monotonic_timestamp - pce_lastsapsp_timestamp) * 1000 / System_Clock);
|
||||
if(cdda.CDDAStatus == CDDASTATUS_PLAYING && new_read_sec_start == read_sec_start && ((int64)(monotonic_timestamp - pce_lastsapsp_timestamp) * 1000 / System_Clock) < 190)
|
||||
{
|
||||
pce_lastsapsp_timestamp = monotonic_timestamp;
|
||||
|
||||
SendStatusAndMessage(STATUS_GOOD, 0x00);
|
||||
CDIRQCallback(SCSICD_IRQ_DATA_TRANSFER_DONE);
|
||||
return;
|
||||
}
|
||||
|
||||
pce_lastsapsp_timestamp = monotonic_timestamp;
|
||||
|
||||
read_sec = read_sec_start = new_read_sec_start;
|
||||
read_sec_end = toc.tracks[100].lba;
|
||||
|
||||
|
||||
cdda.CDDAReadPos = 588;
|
||||
|
||||
cdda.CDDAStatus = CDDASTATUS_PAUSED;
|
||||
cdda.PlayMode = PLAYMODE_SILENT;
|
||||
|
||||
if(cdb[1])
|
||||
{
|
||||
cdda.PlayMode = PLAYMODE_NORMAL;
|
||||
cdda.CDDAStatus = CDDASTATUS_PLAYING;
|
||||
}
|
||||
|
||||
if(read_sec < toc.tracks[100].lba)
|
||||
Cur_CDIF->HintReadSector(read_sec);
|
||||
|
||||
SendStatusAndMessage(STATUS_GOOD, 0x00);
|
||||
CDIRQCallback(SCSICD_IRQ_DATA_TRANSFER_DONE);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/********************************************************
|
||||
* *
|
||||
* PC Engine CD Command 0xD9 - SAPEP *
|
||||
* *
|
||||
********************************************************/
|
||||
static void DoNEC_PCE_SAPEP(const uint8 *cdb)
|
||||
{
|
||||
uint32 new_read_sec_end;
|
||||
|
||||
//printf("Set audio end: %02x %02x %02x %02x %02x %02x %02x\n", cdb[9], cdb[1], cdb[2], cdb[3], cdb[4], cdb[5], cdb[6]);
|
||||
|
||||
switch (cdb[9] & 0xc0)
|
||||
{
|
||||
default: //SCSIDBG("Unknown SAPEP 9: %02x\n", cdb[9]);
|
||||
|
||||
case 0x00:
|
||||
new_read_sec_end = (cdb[3] << 16) | (cdb[4] << 8) | cdb[5];
|
||||
break;
|
||||
|
||||
case 0x40:
|
||||
new_read_sec_end = BCD_to_U8(cdb[4]) + 75 * (BCD_to_U8(cdb[3]) + 60 * BCD_to_U8(cdb[2]));
|
||||
new_read_sec_end -= 150;
|
||||
break;
|
||||
|
||||
case 0x80:
|
||||
{
|
||||
int track = BCD_to_U8(cdb[2]);
|
||||
|
||||
if(!track)
|
||||
track = 1;
|
||||
else if(track >= toc.last_track + 1)
|
||||
track = 100;
|
||||
new_read_sec_end = toc.tracks[track].lba;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
read_sec_end = new_read_sec_end;
|
||||
|
||||
switch(cdb[1]) // PCE CD(TODO: Confirm these, and check the mode mask):
|
||||
{
|
||||
default:
|
||||
case 0x03: cdda.PlayMode = PLAYMODE_NORMAL;
|
||||
cdda.CDDAStatus = CDDASTATUS_PLAYING;
|
||||
break;
|
||||
|
||||
case 0x02: cdda.PlayMode = PLAYMODE_INTERRUPT;
|
||||
cdda.CDDAStatus = CDDASTATUS_PLAYING;
|
||||
break;
|
||||
|
||||
case 0x01: cdda.PlayMode = PLAYMODE_LOOP;
|
||||
cdda.CDDAStatus = CDDASTATUS_PLAYING;
|
||||
break;
|
||||
|
||||
case 0x00: cdda.PlayMode = PLAYMODE_SILENT;
|
||||
cdda.CDDAStatus = CDDASTATUS_STOPPED;
|
||||
break;
|
||||
}
|
||||
|
||||
SendStatusAndMessage(STATUS_GOOD, 0x00);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/********************************************************
|
||||
* *
|
||||
* PC Engine CD Command 0xDA - Pause *
|
||||
* *
|
||||
********************************************************/
|
||||
static void DoNEC_PCE_PAUSE(const uint8 *cdb)
|
||||
{
|
||||
if(cdda.CDDAStatus != CDDASTATUS_STOPPED) // Hmm, should we give an error if it tries to pause and it's already paused?
|
||||
{
|
||||
cdda.CDDAStatus = CDDASTATUS_PAUSED;
|
||||
SendStatusAndMessage(STATUS_GOOD, 0x00);
|
||||
}
|
||||
else // Definitely give an error if it tries to pause when no track is playing!
|
||||
{
|
||||
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_AUDIO_NOT_PLAYING);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/********************************************************
|
||||
* *
|
||||
* PC Engine CD Command 0xDD - Read Subchannel Q *
|
||||
* *
|
||||
********************************************************/
|
||||
static void DoNEC_PCE_READSUBQ(const uint8 *cdb)
|
||||
{
|
||||
uint8 *SubQBuf = cd.SubQBuf[QMode_Time];
|
||||
uint8 data_in[8192];
|
||||
|
||||
memset(data_in, 0x00, 10);
|
||||
|
||||
data_in[2] = SubQBuf[1]; // Track
|
||||
data_in[3] = SubQBuf[2]; // Index
|
||||
data_in[4] = SubQBuf[3]; // M(rel)
|
||||
data_in[5] = SubQBuf[4]; // S(rel)
|
||||
data_in[6] = SubQBuf[5]; // F(rel)
|
||||
data_in[7] = SubQBuf[7]; // M(abs)
|
||||
data_in[8] = SubQBuf[8]; // S(abs)
|
||||
data_in[9] = SubQBuf[9]; // F(abs)
|
||||
|
||||
if(cdda.CDDAStatus == CDDASTATUS_PAUSED)
|
||||
data_in[0] = 2; // Pause
|
||||
else if(cdda.CDDAStatus == CDDASTATUS_PLAYING || cdda.CDDAStatus == CDDASTATUS_SCANNING) // FIXME: Is this the correct status code for scanning playback?
|
||||
data_in[0] = 0; // Playing
|
||||
else
|
||||
data_in[0] = 3; // Stopped
|
||||
|
||||
DoSimpleDataIn(data_in, 10);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/********************************************************
|
||||
* *
|
||||
* PC Engine CD Command 0xDE - Get Directory Info *
|
||||
* *
|
||||
********************************************************/
|
||||
static void DoNEC_PCE_GETDIRINFO(const uint8 *cdb)
|
||||
{
|
||||
// Problems:
|
||||
// Returned data lengths on real PCE are not confirmed.
|
||||
// Mode 0x03 behavior not tested on real PCE
|
||||
|
||||
uint8 data_in[2048];
|
||||
uint32 data_in_size = 0;
|
||||
|
||||
memset(data_in, 0, sizeof(data_in));
|
||||
|
||||
switch(cdb[1])
|
||||
{
|
||||
default: //MDFN_DispMessage("Unknown GETDIRINFO Mode: %02x", cdb[1]);
|
||||
//printf("Unknown GETDIRINFO Mode: %02x", cdb[1]);
|
||||
case 0x0:
|
||||
data_in[0] = U8_to_BCD(toc.first_track);
|
||||
data_in[1] = U8_to_BCD(toc.last_track);
|
||||
|
||||
data_in_size = 2;
|
||||
break;
|
||||
|
||||
case 0x1:
|
||||
{
|
||||
uint8 m, s, f;
|
||||
|
||||
LBA_to_AMSF(toc.tracks[100].lba, &m, &s, &f);
|
||||
|
||||
data_in[0] = U8_to_BCD(m);
|
||||
data_in[1] = U8_to_BCD(s);
|
||||
data_in[2] = U8_to_BCD(f);
|
||||
|
||||
data_in_size = 3;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x2:
|
||||
{
|
||||
uint8 m, s, f;
|
||||
int track = BCD_to_U8(cdb[2]);
|
||||
|
||||
if(!track)
|
||||
track = 1;
|
||||
else if(cdb[2] == 0xAA)
|
||||
{
|
||||
track = 100;
|
||||
}
|
||||
else if(track > 99)
|
||||
{
|
||||
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
|
||||
return;
|
||||
}
|
||||
|
||||
LBA_to_AMSF(toc.tracks[track].lba, &m, &s, &f);
|
||||
|
||||
data_in[0] = U8_to_BCD(m);
|
||||
data_in[1] = U8_to_BCD(s);
|
||||
data_in[2] = U8_to_BCD(f);
|
||||
data_in[3] = toc.tracks[track].control;
|
||||
data_in_size = 4;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
DoSimpleDataIn(data_in, data_in_size);
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,99 +1,99 @@
|
|||
#ifndef __PCFX_SCSICD_H
|
||||
#define __PCFX_SCSICD_H
|
||||
|
||||
typedef int32 scsicd_timestamp_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
// Data bus(FIXME: we should have a variable for the target and the initiator, and OR them together to be truly accurate).
|
||||
uint8 DB;
|
||||
|
||||
uint32 signals;
|
||||
|
||||
// Signals under our(the "target") control.
|
||||
//bool BSY, MSG, CD, REQ, IO;
|
||||
|
||||
// Signals under the control of the initiator(not us!)
|
||||
//bool kingACK, kingRST, kingSEL, kingATN;
|
||||
} scsicd_bus_t;
|
||||
|
||||
extern scsicd_bus_t cd_bus; // Don't access this structure directly by name outside of scsicd.c, but use the macros below.
|
||||
|
||||
// Signals under our(the "target") control.
|
||||
#define SCSICD_IO_mask 0x001
|
||||
#define SCSICD_CD_mask 0x002
|
||||
#define SCSICD_MSG_mask 0x004
|
||||
#define SCSICD_REQ_mask 0x008
|
||||
#define SCSICD_BSY_mask 0x010
|
||||
|
||||
// Signals under the control of the initiator(not us!)
|
||||
#define SCSICD_kingRST_mask 0x020
|
||||
#define SCSICD_kingACK_mask 0x040
|
||||
#define SCSICD_kingATN_mask 0x080
|
||||
#define SCSICD_kingSEL_mask 0x100
|
||||
|
||||
#define BSY_signal ((const bool)(cd_bus.signals & SCSICD_BSY_mask))
|
||||
#define ACK_signal ((const bool)(cd_bus.signals & SCSICD_kingACK_mask))
|
||||
#define RST_signal ((const bool)(cd_bus.signals & SCSICD_kingRST_mask))
|
||||
#define MSG_signal ((const bool)(cd_bus.signals & SCSICD_MSG_mask))
|
||||
#define SEL_signal ((const bool)(cd_bus.signals & SCSICD_kingSEL_mask))
|
||||
#define REQ_signal ((const bool)(cd_bus.signals & SCSICD_REQ_mask))
|
||||
#define IO_signal ((const bool)(cd_bus.signals & SCSICD_IO_mask))
|
||||
#define CD_signal ((const bool)(cd_bus.signals & SCSICD_CD_mask))
|
||||
#define ATN_signal ((const bool)(cd_bus.signals & SCSICD_kingATN_mask))
|
||||
|
||||
#define DB_signal ((const uint8)cd_bus.DB)
|
||||
|
||||
#define SCSICD_GetDB() DB_signal
|
||||
#define SCSICD_GetBSY() BSY_signal
|
||||
#define SCSICD_GetIO() IO_signal
|
||||
#define SCSICD_GetCD() CD_signal
|
||||
#define SCSICD_GetMSG() MSG_signal
|
||||
#define SCSICD_GetREQ() REQ_signal
|
||||
|
||||
// Should we phase out getting these initiator-driven signals like this(the initiator really should keep track of them itself)?
|
||||
#define SCSICD_GetACK() ACK_signal
|
||||
#define SCSICD_GetRST() RST_signal
|
||||
#define SCSICD_GetSEL() SEL_signal
|
||||
#define SCSICD_GetATN() ATN_signal
|
||||
|
||||
void SCSICD_Power(scsicd_timestamp_t system_timestamp);
|
||||
void SCSICD_SetDB(uint8 data);
|
||||
|
||||
// These SCSICD_Set* functions are kind of misnomers, at least in comparison to the SCSICD_Get* functions...
|
||||
// They will set/clear the bits corresponding to the KING's side of the bus.
|
||||
void SCSICD_SetACK(bool set);
|
||||
void SCSICD_SetSEL(bool set);
|
||||
void SCSICD_SetRST(bool set);
|
||||
void SCSICD_SetATN(bool set);
|
||||
|
||||
uint32 SCSICD_Run(scsicd_timestamp_t);
|
||||
void SCSICD_ResetTS(uint32 ts_base);
|
||||
|
||||
enum
|
||||
{
|
||||
SCSICD_PCE = 1,
|
||||
SCSICD_PCFX
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
SCSICD_IRQ_DATA_TRANSFER_DONE = 1,
|
||||
SCSICD_IRQ_DATA_TRANSFER_READY,
|
||||
SCSICD_IRQ_MAGICAL_REQ,
|
||||
};
|
||||
|
||||
void SCSICD_GetCDDAValues(int16 &left, int16 &right);
|
||||
|
||||
void SCSICD_SetLog(void (*logfunc)(const char *, const char *, ...));
|
||||
|
||||
void SCSICD_Init(int type, int CDDATimeDiv, int32* left_hrbuf, int32* right_hrbuf, uint32 TransferRate, uint32 SystemClock, void (*IRQFunc)(int), void (*SSCFunc)(uint8, int));
|
||||
void SCSICD_Close(void);
|
||||
|
||||
void SCSICD_SetTransferRate(uint32 TransferRate);
|
||||
void SCSICD_SetCDDAVolume(double left, double right);
|
||||
void SCSICD_StateAction(StateMem *sm, const unsigned load, const bool data_only, const char *sname);
|
||||
|
||||
void SCSICD_SetDisc(bool tray_open, CDIF *cdif, bool no_emu_side_effects = false);
|
||||
|
||||
#endif
|
||||
#ifndef __PCFX_SCSICD_H
|
||||
#define __PCFX_SCSICD_H
|
||||
|
||||
typedef int32 scsicd_timestamp_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
// Data bus(FIXME: we should have a variable for the target and the initiator, and OR them together to be truly accurate).
|
||||
uint8 DB;
|
||||
|
||||
uint32 signals;
|
||||
|
||||
// Signals under our(the "target") control.
|
||||
//bool BSY, MSG, CD, REQ, IO;
|
||||
|
||||
// Signals under the control of the initiator(not us!)
|
||||
//bool kingACK, kingRST, kingSEL, kingATN;
|
||||
} scsicd_bus_t;
|
||||
|
||||
extern scsicd_bus_t cd_bus; // Don't access this structure directly by name outside of scsicd.c, but use the macros below.
|
||||
|
||||
// Signals under our(the "target") control.
|
||||
#define SCSICD_IO_mask 0x001
|
||||
#define SCSICD_CD_mask 0x002
|
||||
#define SCSICD_MSG_mask 0x004
|
||||
#define SCSICD_REQ_mask 0x008
|
||||
#define SCSICD_BSY_mask 0x010
|
||||
|
||||
// Signals under the control of the initiator(not us!)
|
||||
#define SCSICD_kingRST_mask 0x020
|
||||
#define SCSICD_kingACK_mask 0x040
|
||||
#define SCSICD_kingATN_mask 0x080
|
||||
#define SCSICD_kingSEL_mask 0x100
|
||||
|
||||
#define BSY_signal ((const bool)(cd_bus.signals & SCSICD_BSY_mask))
|
||||
#define ACK_signal ((const bool)(cd_bus.signals & SCSICD_kingACK_mask))
|
||||
#define RST_signal ((const bool)(cd_bus.signals & SCSICD_kingRST_mask))
|
||||
#define MSG_signal ((const bool)(cd_bus.signals & SCSICD_MSG_mask))
|
||||
#define SEL_signal ((const bool)(cd_bus.signals & SCSICD_kingSEL_mask))
|
||||
#define REQ_signal ((const bool)(cd_bus.signals & SCSICD_REQ_mask))
|
||||
#define IO_signal ((const bool)(cd_bus.signals & SCSICD_IO_mask))
|
||||
#define CD_signal ((const bool)(cd_bus.signals & SCSICD_CD_mask))
|
||||
#define ATN_signal ((const bool)(cd_bus.signals & SCSICD_kingATN_mask))
|
||||
|
||||
#define DB_signal ((const uint8)cd_bus.DB)
|
||||
|
||||
#define SCSICD_GetDB() DB_signal
|
||||
#define SCSICD_GetBSY() BSY_signal
|
||||
#define SCSICD_GetIO() IO_signal
|
||||
#define SCSICD_GetCD() CD_signal
|
||||
#define SCSICD_GetMSG() MSG_signal
|
||||
#define SCSICD_GetREQ() REQ_signal
|
||||
|
||||
// Should we phase out getting these initiator-driven signals like this(the initiator really should keep track of them itself)?
|
||||
#define SCSICD_GetACK() ACK_signal
|
||||
#define SCSICD_GetRST() RST_signal
|
||||
#define SCSICD_GetSEL() SEL_signal
|
||||
#define SCSICD_GetATN() ATN_signal
|
||||
|
||||
void SCSICD_Power(scsicd_timestamp_t system_timestamp);
|
||||
void SCSICD_SetDB(uint8 data);
|
||||
|
||||
// These SCSICD_Set* functions are kind of misnomers, at least in comparison to the SCSICD_Get* functions...
|
||||
// They will set/clear the bits corresponding to the KING's side of the bus.
|
||||
void SCSICD_SetACK(bool set);
|
||||
void SCSICD_SetSEL(bool set);
|
||||
void SCSICD_SetRST(bool set);
|
||||
void SCSICD_SetATN(bool set);
|
||||
|
||||
uint32 SCSICD_Run(scsicd_timestamp_t);
|
||||
void SCSICD_ResetTS(uint32 ts_base);
|
||||
|
||||
enum
|
||||
{
|
||||
SCSICD_PCE = 1,
|
||||
SCSICD_PCFX
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
SCSICD_IRQ_DATA_TRANSFER_DONE = 1,
|
||||
SCSICD_IRQ_DATA_TRANSFER_READY,
|
||||
SCSICD_IRQ_MAGICAL_REQ,
|
||||
};
|
||||
|
||||
void SCSICD_GetCDDAValues(int16 &left, int16 &right);
|
||||
|
||||
void SCSICD_SetLog(void (*logfunc)(const char *, const char *, ...));
|
||||
|
||||
void SCSICD_Init(int type, int CDDATimeDiv, int32* left_hrbuf, int32* right_hrbuf, uint32 TransferRate, uint32 SystemClock, void (*IRQFunc)(int), void (*SSCFunc)(uint8, int));
|
||||
void SCSICD_Close(void);
|
||||
|
||||
void SCSICD_SetTransferRate(uint32 TransferRate);
|
||||
void SCSICD_SetCDDAVolume(double left, double right);
|
||||
void SCSICD_StateAction(StateMem *sm, const unsigned load, const bool data_only, const char *sname);
|
||||
|
||||
void SCSICD_SetDisc(bool tray_open, CDIF *cdif, bool no_emu_side_effects = false);
|
||||
|
||||
#endif
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,270 @@
|
|||
/*************************************************************************
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 1998 Bjorn Reese and Daniel Stenberg.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND
|
||||
* CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER.
|
||||
*
|
||||
*************************************************************************
|
||||
*
|
||||
* http://ctrio.sourceforge.net/
|
||||
*
|
||||
************************************************************************/
|
||||
|
||||
#ifndef TRIO_TRIO_H
|
||||
#define TRIO_TRIO_H
|
||||
|
||||
#if !defined(WITHOUT_TRIO)
|
||||
|
||||
/*
|
||||
* Use autoconf defines if present. Packages using trio must define
|
||||
* HAVE_CONFIG_H as a compiler option themselves.
|
||||
*/
|
||||
#if defined(HAVE_CONFIG_H)
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include "triop.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Error codes.
|
||||
*
|
||||
* Remember to add a textual description to trio_strerror.
|
||||
*/
|
||||
enum {
|
||||
TRIO_EOF = 1,
|
||||
TRIO_EINVAL = 2,
|
||||
TRIO_ETOOMANY = 3,
|
||||
TRIO_EDBLREF = 4,
|
||||
TRIO_EGAP = 5,
|
||||
TRIO_ENOMEM = 6,
|
||||
TRIO_ERANGE = 7,
|
||||
TRIO_ERRNO = 8,
|
||||
TRIO_ECUSTOM = 9
|
||||
};
|
||||
|
||||
/* Error macros */
|
||||
#define TRIO_ERROR_CODE(x) ((-(x)) & 0x00FF)
|
||||
#define TRIO_ERROR_POSITION(x) ((-(x)) >> 8)
|
||||
#define TRIO_ERROR_NAME(x) trio_strerror(x)
|
||||
|
||||
/* Argument function types */
|
||||
enum {
|
||||
TRIO_TYPE_POINTER = 1,
|
||||
TRIO_TYPE_CHAR = 2,
|
||||
TRIO_TYPE_SHORT = 3,
|
||||
TRIO_TYPE_INT = 4,
|
||||
TRIO_TYPE_LONG = 5,
|
||||
TRIO_TYPE_ULONGLONG = 6,
|
||||
TRIO_TYPE_UINTMAX = 7,
|
||||
TRIO_TYPE_PTRDIFF = 8,
|
||||
TRIO_TYPE_SIZE = 9,
|
||||
TRIO_TYPE_PCHAR = 10,
|
||||
TRIO_TYPE_PWCHAR = 11,
|
||||
TRIO_TYPE_FLOAT = 12,
|
||||
TRIO_TYPE_DOUBLE = 13,
|
||||
TRIO_TYPE_LONGDOUBLE = 14
|
||||
};
|
||||
|
||||
typedef trio_pointer_t (*trio_argfunc_t) TRIO_PROTO((trio_pointer_t, int, int));
|
||||
typedef int (*trio_outstream_t) TRIO_PROTO((trio_pointer_t, int));
|
||||
typedef int (*trio_instream_t) TRIO_PROTO((trio_pointer_t));
|
||||
|
||||
TRIO_CONST char *trio_strerror TRIO_PROTO((int));
|
||||
|
||||
/*************************************************************************
|
||||
* Print Functions
|
||||
*/
|
||||
|
||||
#if defined(TRIO_COMPILER_GCC) && !TRIO_EXTENSION
|
||||
# define TRIO_PROTO_PRINTF(x,a) TRIO_PROTO(x) __attribute__ ((format (gnu_printf, a, a+1)))
|
||||
# define TRIO_PROTO_SCANF(x,a) TRIO_PROTO(x) __attribute__ ((format (gnu_scanf, a, a+1)))
|
||||
#else
|
||||
# define TRIO_PROTO_PRINTF(x,a) TRIO_PROTO(x)
|
||||
# define TRIO_PROTO_SCANF(x,a) TRIO_PROTO(x)
|
||||
#endif
|
||||
|
||||
int trio_printf TRIO_PROTO_PRINTF((TRIO_CONST char *format, ...), 1);
|
||||
int trio_vprintf TRIO_PROTO((TRIO_CONST char *format, va_list args));
|
||||
int trio_printfv TRIO_PROTO((TRIO_CONST char *format, trio_pointer_t *args));
|
||||
|
||||
int trio_fprintf TRIO_PROTO_PRINTF((FILE *file, TRIO_CONST char *format, ...), 2);
|
||||
int trio_vfprintf TRIO_PROTO((FILE *file, TRIO_CONST char *format, va_list args));
|
||||
int trio_fprintfv TRIO_PROTO((FILE *file, TRIO_CONST char *format, trio_pointer_t *args));
|
||||
|
||||
int trio_dprintf TRIO_PROTO_PRINTF((int fd, TRIO_CONST char *format, ...), 2);
|
||||
int trio_vdprintf TRIO_PROTO((int fd, TRIO_CONST char *format, va_list args));
|
||||
int trio_dprintfv TRIO_PROTO((int fd, TRIO_CONST char *format, trio_pointer_t *args));
|
||||
|
||||
int trio_cprintf TRIO_PROTO_PRINTF((trio_outstream_t stream, trio_pointer_t closure,
|
||||
TRIO_CONST char *format, ...),
|
||||
3);
|
||||
int trio_vcprintf TRIO_PROTO((trio_outstream_t stream, trio_pointer_t closure,
|
||||
TRIO_CONST char *format, va_list args));
|
||||
int trio_cprintfv TRIO_PROTO((trio_outstream_t stream, trio_pointer_t closure,
|
||||
TRIO_CONST char *format, trio_pointer_t *args));
|
||||
int trio_cprintff TRIO_PROTO((trio_outstream_t stream, trio_pointer_t closure,
|
||||
TRIO_CONST char *format,
|
||||
trio_argfunc_t func, trio_pointer_t context));
|
||||
|
||||
int trio_sprintf TRIO_PROTO_PRINTF((char *buffer, TRIO_CONST char *format, ...), 2);
|
||||
int trio_vsprintf TRIO_PROTO((char *buffer, TRIO_CONST char *format, va_list args));
|
||||
int trio_sprintfv TRIO_PROTO((char *buffer, TRIO_CONST char *format, trio_pointer_t *args));
|
||||
|
||||
int trio_snprintf TRIO_PROTO_PRINTF((char *buffer, size_t max, TRIO_CONST char *format, ...), 3);
|
||||
int trio_vsnprintf TRIO_PROTO((char *buffer, size_t bufferSize, TRIO_CONST char *format,
|
||||
va_list args));
|
||||
int trio_snprintfv TRIO_PROTO((char *buffer, size_t bufferSize, TRIO_CONST char *format,
|
||||
trio_pointer_t *args));
|
||||
|
||||
int trio_snprintfcat TRIO_PROTO_PRINTF((char *buffer, size_t max, TRIO_CONST char *format, ...), 3);
|
||||
int trio_vsnprintfcat TRIO_PROTO((char *buffer, size_t bufferSize, TRIO_CONST char *format,
|
||||
va_list args));
|
||||
|
||||
#if defined(TRIO_DEPRECATED)
|
||||
char *trio_aprintf TRIO_PROTO_PRINTF((TRIO_CONST char *format, ...), 1);
|
||||
char *trio_vaprintf TRIO_PROTO((TRIO_CONST char *format, va_list args));
|
||||
#endif
|
||||
|
||||
int trio_asprintf TRIO_PROTO_PRINTF((char **ret, TRIO_CONST char *format, ...), 2);
|
||||
int trio_vasprintf TRIO_PROTO((char **ret, TRIO_CONST char *format, va_list args));
|
||||
int trio_asprintfv TRIO_PROTO((char **result, TRIO_CONST char *format, trio_pointer_t * args));
|
||||
|
||||
/*************************************************************************
|
||||
* Scan Functions
|
||||
*/
|
||||
int trio_scanf TRIO_PROTO_SCANF((TRIO_CONST char *format, ...), 1);
|
||||
int trio_vscanf TRIO_PROTO((TRIO_CONST char *format, va_list args));
|
||||
int trio_scanfv TRIO_PROTO((TRIO_CONST char *format, void **args));
|
||||
|
||||
int trio_fscanf TRIO_PROTO_SCANF((FILE *file, TRIO_CONST char *format, ...), 2);
|
||||
int trio_vfscanf TRIO_PROTO((FILE *file, TRIO_CONST char *format, va_list args));
|
||||
int trio_fscanfv TRIO_PROTO((FILE *file, TRIO_CONST char *format, void **args));
|
||||
|
||||
int trio_dscanf TRIO_PROTO_SCANF((int fd, TRIO_CONST char *format, ...), 2);
|
||||
int trio_vdscanf TRIO_PROTO((int fd, TRIO_CONST char *format, va_list args));
|
||||
int trio_dscanfv TRIO_PROTO((int fd, TRIO_CONST char *format, void **args));
|
||||
|
||||
int trio_cscanf TRIO_PROTO_SCANF((trio_instream_t stream, trio_pointer_t closure,
|
||||
TRIO_CONST char *format, ...),
|
||||
3);
|
||||
int trio_vcscanf TRIO_PROTO((trio_instream_t stream, trio_pointer_t closure,
|
||||
TRIO_CONST char *format, va_list args));
|
||||
int trio_cscanfv TRIO_PROTO((trio_instream_t stream, trio_pointer_t closure,
|
||||
TRIO_CONST char *format, void **args));
|
||||
int trio_cscanff TRIO_PROTO((trio_instream_t stream, trio_pointer_t closure,
|
||||
TRIO_CONST char *format,
|
||||
trio_argfunc_t func, trio_pointer_t context));
|
||||
|
||||
int trio_sscanf TRIO_PROTO_SCANF((TRIO_CONST char *buffer, TRIO_CONST char *format, ...), 2);
|
||||
int trio_vsscanf TRIO_PROTO((TRIO_CONST char *buffer, TRIO_CONST char *format, va_list args));
|
||||
int trio_sscanfv TRIO_PROTO((TRIO_CONST char *buffer, TRIO_CONST char *format, void **args));
|
||||
|
||||
/*************************************************************************
|
||||
* Locale Functions
|
||||
*/
|
||||
void trio_locale_set_decimal_point TRIO_PROTO((char *decimalPoint));
|
||||
void trio_locale_set_thousand_separator TRIO_PROTO((char *thousandSeparator));
|
||||
void trio_locale_set_grouping TRIO_PROTO((char *grouping));
|
||||
|
||||
/*************************************************************************
|
||||
* Renaming
|
||||
*/
|
||||
#ifdef TRIO_REPLACE_STDIO
|
||||
/* Replace the <stdio.h> functions */
|
||||
#ifndef HAVE_PRINTF
|
||||
# undef printf
|
||||
# define printf trio_printf
|
||||
#endif
|
||||
#ifndef HAVE_VPRINTF
|
||||
# undef vprintf
|
||||
# define vprintf trio_vprintf
|
||||
#endif
|
||||
#ifndef HAVE_FPRINTF
|
||||
# undef fprintf
|
||||
# define fprintf trio_fprintf
|
||||
#endif
|
||||
#ifndef HAVE_VFPRINTF
|
||||
# undef vfprintf
|
||||
# define vfprintf trio_vfprintf
|
||||
#endif
|
||||
#ifndef HAVE_SPRINTF
|
||||
# undef sprintf
|
||||
# define sprintf trio_sprintf
|
||||
#endif
|
||||
#ifndef HAVE_VSPRINTF
|
||||
# undef vsprintf
|
||||
# define vsprintf trio_vsprintf
|
||||
#endif
|
||||
#ifndef HAVE_SNPRINTF
|
||||
# undef snprintf
|
||||
# define snprintf trio_snprintf
|
||||
#endif
|
||||
#ifndef HAVE_VSNPRINTF
|
||||
# undef vsnprintf
|
||||
# define vsnprintf trio_vsnprintf
|
||||
#endif
|
||||
#ifndef HAVE_SCANF
|
||||
# undef scanf
|
||||
# define scanf trio_scanf
|
||||
#endif
|
||||
#ifndef HAVE_VSCANF
|
||||
# undef vscanf
|
||||
# define vscanf trio_vscanf
|
||||
#endif
|
||||
#ifndef HAVE_FSCANF
|
||||
# undef fscanf
|
||||
# define fscanf trio_fscanf
|
||||
#endif
|
||||
#ifndef HAVE_VFSCANF
|
||||
# undef vfscanf
|
||||
# define vfscanf trio_vfscanf
|
||||
#endif
|
||||
#ifndef HAVE_SSCANF
|
||||
# undef sscanf
|
||||
# define sscanf trio_sscanf
|
||||
#endif
|
||||
#ifndef HAVE_VSSCANF
|
||||
# undef vsscanf
|
||||
# define vsscanf trio_vsscanf
|
||||
#endif
|
||||
/* These aren't stdio functions, but we make them look similar */
|
||||
#undef dprintf
|
||||
#define dprintf trio_dprintf
|
||||
#undef vdprintf
|
||||
#define vdprintf trio_vdprintf
|
||||
#undef aprintf
|
||||
#define aprintf trio_aprintf
|
||||
#undef vaprintf
|
||||
#define vaprintf trio_vaprintf
|
||||
#undef asprintf
|
||||
#define asprintf trio_asprintf
|
||||
#undef vasprintf
|
||||
#define vasprintf trio_vasprintf
|
||||
#undef dscanf
|
||||
#define dscanf trio_dscanf
|
||||
#undef vdscanf
|
||||
#define vdscanf trio_vdscanf
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* WITHOUT_TRIO */
|
||||
|
||||
#endif /* TRIO_TRIO_H */
|
|
@ -0,0 +1,375 @@
|
|||
/*************************************************************************
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2001 Bjorn Reese <breese@users.sourceforge.net>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND
|
||||
* CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER.
|
||||
*
|
||||
************************************************************************/
|
||||
|
||||
#ifndef TRIO_TRIODEF_H
|
||||
#define TRIO_TRIODEF_H
|
||||
|
||||
/*************************************************************************
|
||||
* Compiler support detection
|
||||
*/
|
||||
|
||||
#if defined(__GNUC__)
|
||||
# define TRIO_COMPILER_GCC
|
||||
#endif
|
||||
|
||||
#if defined(__SUNPRO_CC)
|
||||
# define TRIO_COMPILER_SUNPRO __SUNPRO_CC
|
||||
#else
|
||||
# if defined(__SUNPRO_C)
|
||||
# define TRIO_COMPILER_SUNPRO __SUNPRO_C
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(__xlC__) || defined(__IBMC__) || defined(__IBMCPP__)
|
||||
# define TRIO_COMPILER_XLC
|
||||
#else
|
||||
# if defined(_AIX) && !defined(__GNUC__)
|
||||
# define TRIO_COMPILER_XLC /* Workaround for old xlc */
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(__DECC) || defined(__DECCXX)
|
||||
# define TRIO_COMPILER_DECC
|
||||
#else
|
||||
# if defined(__osf__) && defined(__LANGUAGE_C__) && !defined(__GNUC__)
|
||||
# define TRIO_COMPILER_DECC /* Workaround for old DEC C compilers */
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(__HP_aCC) || defined(__HP_cc)
|
||||
# define TRIO_COMPILER_HP
|
||||
#endif
|
||||
|
||||
#if defined(sgi) || defined(__sgi)
|
||||
# define TRIO_COMPILER_MIPSPRO
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# define TRIO_COMPILER_MSVC
|
||||
#endif
|
||||
|
||||
#if defined(__BORLANDC__)
|
||||
# define TRIO_COMPILER_BCB
|
||||
#endif
|
||||
|
||||
/*************************************************************************
|
||||
* Platform support detection
|
||||
*/
|
||||
|
||||
#if defined(VMS) || defined(__VMS)
|
||||
# define TRIO_PLATFORM_VMS
|
||||
#endif
|
||||
|
||||
#if defined(unix) || defined(__unix) || defined(__unix__)
|
||||
# define TRIO_PLATFORM_UNIX
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_COMPILER_XLC) || defined(_AIX)
|
||||
# define TRIO_PLATFORM_UNIX
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_COMPILER_DECC) || defined(__osf___)
|
||||
# if !defined(TRIO_PLATFORM_VMS)
|
||||
# define TRIO_PLATFORM_UNIX
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(__NetBSD__)
|
||||
# define TRIO_PLATFORM_UNIX
|
||||
#endif
|
||||
|
||||
#if defined(__Lynx__)
|
||||
# define TRIO_PLATFORM_UNIX
|
||||
# define TRIO_PLATFORM_LYNX
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
# define TRIO_PLATFORM_UNIX
|
||||
#endif
|
||||
|
||||
#if defined(__QNX__)
|
||||
# define TRIO_PLATFORM_UNIX
|
||||
# define TRIO_PLATFORM_QNX
|
||||
#endif
|
||||
|
||||
#if defined(__CYGWIN__)
|
||||
# define TRIO_PLATFORM_UNIX
|
||||
#endif
|
||||
|
||||
#if defined(AMIGA) && defined(TRIO_COMPILER_GCC)
|
||||
# define TRIO_PLATFORM_UNIX
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_COMPILER_MSVC) || defined(WIN32) || defined(_WIN32)
|
||||
# define TRIO_PLATFORM_WIN32
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32_WCE)
|
||||
# define TRIO_PLATFORM_WINCE
|
||||
#endif
|
||||
|
||||
#if defined(mpeix) || defined(__mpexl)
|
||||
# define TRIO_PLATFORM_MPEIX
|
||||
#endif
|
||||
|
||||
#if defined(_AIX)
|
||||
# define TRIO_PLATFORM_AIX
|
||||
#endif
|
||||
|
||||
#if defined(__hpux)
|
||||
# define TRIO_PLATFORM_HPUX
|
||||
#endif
|
||||
|
||||
#if defined(sun) || defined(__sun__)
|
||||
# if defined(__SVR4) || defined(__svr4__)
|
||||
# define TRIO_PLATFORM_SOLARIS
|
||||
# else
|
||||
# define TRIO_PLATFORM_SUNOS
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(__powerpc) || defined(__powerpc__) || defined(_ARCH_PPC)
|
||||
# define TRIO_CPU_POWERPC
|
||||
#endif
|
||||
|
||||
#if defined(__sparc) || defined(__sparc__)
|
||||
# define TRIO_CPU_SPARC
|
||||
#endif
|
||||
|
||||
#if defined(__s390x__) || defined(__zarch__) || defined(__SYSC_ZARCH__)
|
||||
# define TRIO_CPU_SYSTEMZ
|
||||
#endif
|
||||
|
||||
/*************************************************************************
|
||||
* Standards support detection
|
||||
*/
|
||||
|
||||
#if defined(__STDC__) \
|
||||
|| defined(_MSC_EXTENSIONS) \
|
||||
|| defined(TRIO_COMPILER_BCB)
|
||||
# define PREDEF_STANDARD_C89
|
||||
#endif
|
||||
#if defined(__STDC_VERSION__)
|
||||
# define PREDEF_STANDARD_C90
|
||||
#endif
|
||||
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199409L)
|
||||
# define PREDEF_STANDARD_C94
|
||||
#endif
|
||||
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
|
||||
# define PREDEF_STANDARD_C99
|
||||
#endif
|
||||
#if defined(TRIO_COMPILER_SUNPRO) && (TRIO_COMPILER_SUNPRO >= 0x420)
|
||||
# if !defined(PREDEF_STANDARD_C94)
|
||||
# define PREDEF_STANDARD_C94
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(__cplusplus)
|
||||
# define PREDEF_STANDARD_CXX
|
||||
#endif
|
||||
#if defined(__cplusplus) && (__cplusplus >= 199711L)
|
||||
# define PREDEF_STANDARD_CXX89
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_PLATFORM_UNIX)
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#if defined(_POSIX_VERSION)
|
||||
# define PREDEF_STANDARD_POSIX _POSIX_VERSION
|
||||
# if (_POSIX_VERSION >= 199506L)
|
||||
# define PREDEF_STANDARD_POSIX_1996
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if (_XOPEN_VERSION - 0 >= 3) || defined(_XOPEN_XPG3)
|
||||
# define PREDEF_STANDARD_XPG3
|
||||
#endif
|
||||
#if (_XOPEN_VERSION - 0 >= 4) || defined(_XOPEN_XPG4)
|
||||
# define PREDEF_STANDARD_XPG4
|
||||
#endif
|
||||
#if (_XOPEN_VERSION - 0 > 4) \
|
||||
|| (defined(_XOPEN_UNIX) && (_XOPEN_VERSION - 0 == 4))
|
||||
# define PREDEF_STANDARD_UNIX95
|
||||
#endif
|
||||
#if (_XOPEN_VERSION - 0 >= 500)
|
||||
# define PREDEF_STANDARD_UNIX98
|
||||
#endif
|
||||
#if (_XOPEN_VERSION - 0 >= 600)
|
||||
# define PREDEF_STANDARD_UNIX03
|
||||
#endif
|
||||
|
||||
/*************************************************************************
|
||||
* Generic defines
|
||||
*/
|
||||
|
||||
#if !defined(TRIO_PUBLIC)
|
||||
/* Based on http://gcc.gnu.org/wiki/Visibility */
|
||||
# if defined(TRIO_PLATFORM_WIN32) || defined (__CYGWIN__)
|
||||
# if defined(BUILDING_DLL)
|
||||
# if defined(TRIO_COMPILER_GCC)
|
||||
# define TRIO_PUBLIC __attribute__ ((dllexport))
|
||||
# else
|
||||
# define TRIO_PUBLIC __declspec(dllexport)
|
||||
# endif
|
||||
# else
|
||||
# if defined(TRIO_COMPILER_GCC)
|
||||
# define TRIO_PUBLIC __attribute__ ((dllimport))
|
||||
# else
|
||||
# define TRIO_PUBLIC __declspec(dllimport)
|
||||
# endif
|
||||
# endif
|
||||
# else
|
||||
# if defined(TRIO_COMPILER_GCC) && __GNUC__ >= 4
|
||||
# define TRIO_PUBLIC __attribute__ ((visibility ("default")))
|
||||
# define TRIO_PRIVATE __attribute__ ((visibility ("hidden")))
|
||||
# else
|
||||
# define TRIO_PUBLIC
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
#if !defined(TRIO_PRIVATE)
|
||||
# define TRIO_PRIVATE static
|
||||
#endif
|
||||
|
||||
#if !(defined(PREDEF_STANDARD_C89) || defined(PREDEF_STANDARD_CXX))
|
||||
# define TRIO_COMPILER_ANCIENT
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_COMPILER_ANCIENT)
|
||||
# define TRIO_CONST
|
||||
# define TRIO_VOLATILE
|
||||
# define TRIO_SIGNED
|
||||
typedef double trio_long_double_t;
|
||||
typedef char * trio_pointer_t;
|
||||
# define TRIO_SUFFIX_LONG(x) x
|
||||
# define TRIO_PROTO(x) ()
|
||||
# define TRIO_NOARGS
|
||||
# define TRIO_ARGS1(list,a1) list a1;
|
||||
# define TRIO_ARGS2(list,a1,a2) list a1; a2;
|
||||
# define TRIO_ARGS3(list,a1,a2,a3) list a1; a2; a3;
|
||||
# define TRIO_ARGS4(list,a1,a2,a3,a4) list a1; a2; a3; a4;
|
||||
# define TRIO_ARGS5(list,a1,a2,a3,a4,a5) list a1; a2; a3; a4; a5;
|
||||
# define TRIO_ARGS6(list,a1,a2,a3,a4,a5,a6) list a1; a2; a3; a4; a5; a6;
|
||||
# define TRIO_ARGS7(list,a1,a2,a3,a4,a5,a6,a7) list a1; a2; a3; a4; a5; a6; a7;
|
||||
# define TRIO_ARGS8(list,a1,a2,a3,a4,a5,a6,a7,a8) list a1; a2; a3; a4; a5; a6; a7; a8;
|
||||
# define TRIO_VARGS2(list,a1,a2) list a1; a2
|
||||
# define TRIO_VARGS3(list,a1,a2,a3) list a1; a2; a3
|
||||
# define TRIO_VARGS4(list,a1,a2,a3,a4) list a1; a2; a3; a4
|
||||
# define TRIO_VARGS5(list,a1,a2,a3,a4,a5) list a1; a2; a3; a4; a5
|
||||
# define TRIO_VA_DECL va_dcl
|
||||
# define TRIO_VA_START(x,y) va_start(x)
|
||||
# define TRIO_VA_END(x) va_end(x)
|
||||
#else /* ANSI C */
|
||||
# define TRIO_CONST const
|
||||
# define TRIO_VOLATILE volatile
|
||||
# define TRIO_SIGNED signed
|
||||
typedef long double trio_long_double_t;
|
||||
typedef void * trio_pointer_t;
|
||||
# define TRIO_SUFFIX_LONG(x) x ## L
|
||||
# define TRIO_PROTO(x) x
|
||||
# define TRIO_NOARGS void
|
||||
# define TRIO_ARGS1(list,a1) (a1)
|
||||
# define TRIO_ARGS2(list,a1,a2) (a1,a2)
|
||||
# define TRIO_ARGS3(list,a1,a2,a3) (a1,a2,a3)
|
||||
# define TRIO_ARGS4(list,a1,a2,a3,a4) (a1,a2,a3,a4)
|
||||
# define TRIO_ARGS5(list,a1,a2,a3,a4,a5) (a1,a2,a3,a4,a5)
|
||||
# define TRIO_ARGS6(list,a1,a2,a3,a4,a5,a6) (a1,a2,a3,a4,a5,a6)
|
||||
# define TRIO_ARGS7(list,a1,a2,a3,a4,a5,a6,a7) (a1,a2,a3,a4,a5,a6,a7)
|
||||
# define TRIO_ARGS8(list,a1,a2,a3,a4,a5,a6,a7,a8) (a1,a2,a3,a4,a5,a6,a7,a8)
|
||||
# define TRIO_VARGS2 TRIO_ARGS2
|
||||
# define TRIO_VARGS3 TRIO_ARGS3
|
||||
# define TRIO_VARGS4 TRIO_ARGS4
|
||||
# define TRIO_VARGS5 TRIO_ARGS5
|
||||
# define TRIO_VA_DECL ...
|
||||
# define TRIO_VA_START(x,y) va_start(x,y)
|
||||
# define TRIO_VA_END(x) va_end(x)
|
||||
#endif
|
||||
|
||||
#if defined(PREDEF_STANDARD_C99) || defined(PREDEF_STANDARD_CXX)
|
||||
# define TRIO_INLINE inline
|
||||
#else
|
||||
# if defined(TRIO_COMPILER_GCC)
|
||||
# define TRIO_INLINE __inline__
|
||||
# endif
|
||||
# if defined(TRIO_COMPILER_MSVC)
|
||||
# define TRIO_INLINE _inline
|
||||
# endif
|
||||
# if defined(TRIO_COMPILER_BCB)
|
||||
# define TRIO_INLINE __inline
|
||||
# endif
|
||||
#endif
|
||||
#if !defined(TRIO_INLINE)
|
||||
# define TRIO_INLINE
|
||||
#endif
|
||||
|
||||
/*************************************************************************
|
||||
* Workarounds
|
||||
*/
|
||||
|
||||
#if defined(TRIO_PLATFORM_VMS)
|
||||
/*
|
||||
* Computations done with constants at compile time can trigger these
|
||||
* even when compiling with IEEE enabled.
|
||||
*/
|
||||
# pragma message disable (UNDERFLOW, FLOATOVERFL)
|
||||
|
||||
# if (__CRTL_VER < 80210001)
|
||||
/*
|
||||
* Although the compiler supports C99 language constructs, the C
|
||||
* run-time library does not contain all C99 functions.
|
||||
*/
|
||||
# if defined(PREDEF_STANDARD_C99)
|
||||
# undef PREDEF_STANDARD_C99
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Not all preprocessors supports the LL token.
|
||||
*/
|
||||
#if defined(TRIO_COMPILER_MSVC) || defined(TRIO_COMPILER_BCB)
|
||||
#else
|
||||
# define TRIO_COMPILER_SUPPORTS_LL
|
||||
#endif
|
||||
|
||||
#if defined(__CYGWIN__)
|
||||
/*
|
||||
* Cygwin defines the macros for hosted C99, but does not support certain
|
||||
* long double math functions.
|
||||
*/
|
||||
# include <cygwin/version.h>
|
||||
# define TRIO_CYGWIN_VERSION_API CYGWIN_VERSION_API_MAJOR * 1000 + \
|
||||
CYGWIN_VERSION_API_MINOR
|
||||
/*
|
||||
* Please change the version number below when the Cygwin API supports
|
||||
* long double math functions (powl, fmodl, etc.)
|
||||
*/
|
||||
# if TRIO_CYGWIN_VERSION_API < 99999999
|
||||
# define TRIO_NO_FLOORL 1
|
||||
# define TRIO_NO_CEILL 1
|
||||
# define TRIO_NO_POWL 1
|
||||
# define TRIO_NO_FMODL 1
|
||||
# define TRIO_NO_LOG10L 1
|
||||
# endif
|
||||
#endif
|
||||
|
||||
# if defined(TRIO_CPU_POWERPC) || defined(TRIO_CPU_SPARC) || defined(TRIO_CPU_SYSTEMZ)
|
||||
# define TRIO_DOUBLE_DOUBLE
|
||||
# endif
|
||||
|
||||
#endif /* TRIO_TRIODEF_H */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,183 @@
|
|||
/*************************************************************************
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2001 Bjorn Reese <breese@users.sourceforge.net>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND
|
||||
* CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER.
|
||||
*
|
||||
************************************************************************/
|
||||
|
||||
#ifndef TRIO_TRIONAN_H
|
||||
#define TRIO_TRIONAN_H
|
||||
|
||||
#include "triodef.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if !defined(TRIO_PUBLIC_NAN)
|
||||
# if !defined(TRIO_PUBLIC)
|
||||
# define TRIO_PUBLIC
|
||||
# endif
|
||||
# define TRIO_PUBLIC_NAN TRIO_PUBLIC
|
||||
#endif
|
||||
|
||||
enum {
|
||||
TRIO_FP_INFINITE,
|
||||
TRIO_FP_NAN,
|
||||
TRIO_FP_NORMAL,
|
||||
TRIO_FP_SUBNORMAL,
|
||||
TRIO_FP_ZERO
|
||||
};
|
||||
|
||||
/*************************************************************************
|
||||
* Dependencies
|
||||
*/
|
||||
|
||||
#if defined(TRIO_EMBED_NAN)
|
||||
|
||||
/*
|
||||
* The application that trionan is embedded in must define which functions
|
||||
* it uses.
|
||||
*
|
||||
* The following resolves internal dependencies.
|
||||
*/
|
||||
|
||||
# if defined(TRIO_FUNC_ISNAN) \
|
||||
|| defined(TRIO_FUNC_ISINF)
|
||||
# if !defined(TRIO_FUNC_FPCLASSIFY_AND_SIGNBIT)
|
||||
# define TRIO_FUNC_FPCLASSIFY_AND_SIGNBIT
|
||||
# endif
|
||||
# endif
|
||||
|
||||
# if defined(TRIO_FUNC_NAN)
|
||||
# if !defined(TRIO_FUNC_PINF)
|
||||
# define TRIO_FUNC_PINF
|
||||
# endif
|
||||
# endif
|
||||
|
||||
# if defined(TRIO_FUNC_NINF)
|
||||
# if !defined(TRIO_FUNC_PINF)
|
||||
# define TRIO_FUNC_PINF
|
||||
# endif
|
||||
# endif
|
||||
|
||||
#else
|
||||
|
||||
/*
|
||||
* When trionan is not embedded all all functions are defined.
|
||||
*/
|
||||
|
||||
# define TRIO_FUNC_NAN
|
||||
# define TRIO_FUNC_PINF
|
||||
# define TRIO_FUNC_NINF
|
||||
# define TRIO_FUNC_NZERO
|
||||
# define TRIO_FUNC_ISNAN
|
||||
# define TRIO_FUNC_ISINF
|
||||
# define TRIO_FUNC_ISFINITE
|
||||
# define TRIO_FUNC_SIGNBIT
|
||||
# define TRIO_FUNC_FPCLASSIFY
|
||||
# define TRIO_FUNC_FPCLASSIFY_AND_SIGNBIT
|
||||
|
||||
#endif
|
||||
|
||||
/*************************************************************************
|
||||
* Functions
|
||||
*/
|
||||
|
||||
/*
|
||||
* Return NaN (Not-a-Number).
|
||||
*/
|
||||
#if defined(TRIO_FUNC_NAN)
|
||||
TRIO_PUBLIC_NAN double
|
||||
trio_nan
|
||||
TRIO_PROTO((void));
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Return positive infinity.
|
||||
*/
|
||||
#if defined(TRIO_FUNC_PINF)
|
||||
TRIO_PUBLIC_NAN double
|
||||
trio_pinf
|
||||
TRIO_PROTO((void));
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Return negative infinity.
|
||||
*/
|
||||
#if defined(TRIO_FUNC_NINF)
|
||||
TRIO_PUBLIC_NAN double
|
||||
trio_ninf
|
||||
TRIO_PROTO((void));
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Return negative zero.
|
||||
*/
|
||||
#if defined(TRIO_FUNC_NZERO)
|
||||
TRIO_PUBLIC_NAN double
|
||||
trio_nzero
|
||||
TRIO_PROTO((TRIO_NOARGS));
|
||||
#endif
|
||||
|
||||
/*
|
||||
* If number is a NaN return non-zero, otherwise return zero.
|
||||
*/
|
||||
#if defined(TRIO_FUNC_ISNAN)
|
||||
TRIO_PUBLIC_NAN int
|
||||
trio_isnan
|
||||
TRIO_PROTO((double number));
|
||||
#endif
|
||||
|
||||
/*
|
||||
* If number is positive infinity return 1, if number is negative
|
||||
* infinity return -1, otherwise return 0.
|
||||
*/
|
||||
#if defined(TRIO_FUNC_ISINF)
|
||||
TRIO_PUBLIC_NAN int
|
||||
trio_isinf
|
||||
TRIO_PROTO((double number));
|
||||
#endif
|
||||
|
||||
/*
|
||||
* If number is finite return non-zero, otherwise return zero.
|
||||
*/
|
||||
#if defined(TRIO_FUNC_ISFINITE)
|
||||
TRIO_PUBLIC_NAN int
|
||||
trio_isfinite
|
||||
TRIO_PROTO((double number));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_SIGNBIT)
|
||||
TRIO_PUBLIC_NAN int
|
||||
trio_signbit
|
||||
TRIO_PROTO((double number));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_FPCLASSIFY)
|
||||
TRIO_PUBLIC_NAN int
|
||||
trio_fpclassify
|
||||
TRIO_PROTO((double number));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_FPCLASSIFY_AND_SIGNBIT)
|
||||
TRIO_PUBLIC_NAN int
|
||||
trio_fpclassify_and_signbit
|
||||
TRIO_PROTO((double number, int *is_negative));
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* TRIO_TRIONAN_H */
|
|
@ -0,0 +1,496 @@
|
|||
/*************************************************************************
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2000 Bjorn Reese and Daniel Stenberg.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND
|
||||
* CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER.
|
||||
*
|
||||
************************************************************************
|
||||
*
|
||||
* Private functions, types, etc. used for callback functions.
|
||||
*
|
||||
* The ref pointer is an opaque type and should remain as such.
|
||||
* Private data must only be accessible through the getter and
|
||||
* setter functions.
|
||||
*
|
||||
************************************************************************/
|
||||
|
||||
#ifndef TRIO_TRIOP_H
|
||||
#define TRIO_TRIOP_H
|
||||
|
||||
#include "triodef.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#if defined(TRIO_COMPILER_ANCIENT)
|
||||
# include <varargs.h>
|
||||
#else
|
||||
# include <stdarg.h>
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*************************************************************************
|
||||
* Supported standards
|
||||
*/
|
||||
|
||||
/*
|
||||
* TRIO_C99 (=0 or =1)
|
||||
*
|
||||
* Define this to 0 to disable C99 format specifier extensions, or
|
||||
* define to 1 to enable them. The format specifiers that are
|
||||
* disabled by this switch are labelled with [C99] in the format
|
||||
* specifier documentation.
|
||||
*/
|
||||
#if !defined(TRIO_C99)
|
||||
# define TRIO_C99 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
* TRIO_BSD (=0 or =1)
|
||||
*
|
||||
* Define this to 0 to disable BSD format specifier extensions, or
|
||||
* define to 1 to enable them. The format specifiers that are
|
||||
* disabled by this switch are labelled with [BSD] in the format
|
||||
* specifier documentation.
|
||||
*/
|
||||
#if !defined(TRIO_BSD)
|
||||
# define TRIO_BSD 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
* TRIO_GNU (=0 or =1)
|
||||
*
|
||||
* Define this to 0 to disable GNU format specifier extensions, or
|
||||
* define to 1 to enable them. The format specifiers that are
|
||||
* disabled by this switch are labelled with [GNU] in the format
|
||||
* specifier documentation.
|
||||
*/
|
||||
#if !defined(TRIO_GNU)
|
||||
# define TRIO_GNU 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
* TRIO_MISC (=0 or =1)
|
||||
*
|
||||
* Define this to 0 to disable miscellaneous format specifier
|
||||
* extensions, or define to 1 to enable them. The format specifiers
|
||||
* that are disabled by this switch are labelled with [MISC] in the
|
||||
* format specifier documentation.
|
||||
*/
|
||||
#if !defined(TRIO_MISC)
|
||||
# define TRIO_MISC 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
* TRIO_UNIX98 (=0 or =1)
|
||||
*
|
||||
* Define this to 0 to disable UNIX98 format specifier extensions,
|
||||
* or define to 1 to enable them. The format specifiers that are
|
||||
* disabled by this switch are labelled with [UNIX98] in the format
|
||||
* specifier documentation.
|
||||
*/
|
||||
#if !defined(TRIO_UNIX98)
|
||||
# define TRIO_UNIX98 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
* TRIO_MICROSOFT (=0 or =1)
|
||||
*
|
||||
* Define this to 0 to disable Microsoft Visual C format specifier
|
||||
* extensions, or define to 1 to enable them. The format specifiers
|
||||
* that are disabled by this switch are labelled with [MSVC] in the
|
||||
* format specifier documentation.
|
||||
*/
|
||||
#if !defined(TRIO_MICROSOFT)
|
||||
# define TRIO_MICROSOFT 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
* TRIO_EXTENSION (=0 or =1)
|
||||
*
|
||||
* Define this to 0 to disable Trio-specific extensions, or define
|
||||
* to 1 to enable them. This has two effects: it controls whether
|
||||
* or not the Trio user-defined formating mechanism
|
||||
* (trio_register() etc) is supported, and it enables or disables
|
||||
* Trio's own format specifier extensions. The format specifiers
|
||||
* that are disabled by this switch are labelled with [TRIO] in
|
||||
* the format specifier documentation.
|
||||
*/
|
||||
#if !defined(TRIO_EXTENSION)
|
||||
# define TRIO_EXTENSION 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
* TRIO_DEPRECATED (=0 or =1)
|
||||
*
|
||||
* Define this to 0 to disable deprecated functionality, or define
|
||||
* to 1 to enable them.
|
||||
*/
|
||||
#if !defined(TRIO_DEPRECATED)
|
||||
# define TRIO_DEPRECATED 1
|
||||
#endif
|
||||
|
||||
/*************************************************************************
|
||||
* Features
|
||||
*/
|
||||
|
||||
#if defined(TRIO_SNPRINTF_ONLY)
|
||||
# define TRIO_FEATURE_SCANF 0
|
||||
# define TRIO_FEATURE_FILE 0
|
||||
# define TRIO_FEATURE_STDIO 0
|
||||
# define TRIO_FEATURE_FD 0
|
||||
# define TRIO_FEATURE_DYNAMICSTRING 0
|
||||
# define TRIO_FEATURE_CLOSURE 0
|
||||
# define TRIO_FEATURE_ARGFUNC 0
|
||||
# define TRIO_FEATURE_STRERR 0
|
||||
# define TRIO_FEATURE_LOCALE 0
|
||||
# define TRIO_EMBED_NAN 1
|
||||
# define TRIO_EMBED_STRING 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
* TRIO_FEATURE_SCANF (=0 or =1)
|
||||
*
|
||||
* Define this to 0 to disable all the scanf() variants, or define to 1
|
||||
* to enable them.
|
||||
*/
|
||||
#if !defined(TRIO_FEATURE_SCANF)
|
||||
# define TRIO_FEATURE_SCANF 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
* TRIO_FEATURE_FILE (=0 or =1)
|
||||
*
|
||||
* Define this to 0 to disable compilation of the trio_fprintf() and
|
||||
* trio_fscanf() family of functions, or define to 1 to enable them.
|
||||
*
|
||||
* This may be useful on an embedded platform with no filesystem.
|
||||
* Note that trio_printf() uses fwrite to write to stdout, so if you
|
||||
* do not have an implementation of fwrite() at all then you must also
|
||||
* define TRIO_FEATURE_STDIO to 0.
|
||||
*/
|
||||
#if !defined(TRIO_FEATURE_FILE)
|
||||
# define TRIO_FEATURE_FILE 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
* TRIO_FEATURE_STDIO (=0 or =1)
|
||||
*
|
||||
* Define this to 0 to disable compilation of the trio_printf() and
|
||||
* trio_scanf() family of functions, or define to 1 to enable them.
|
||||
*
|
||||
* This may be useful on an embedded platform with no standard I/O.
|
||||
*/
|
||||
#if !defined(TRIO_FEATURE_STDIO)
|
||||
# define TRIO_FEATURE_STDIO 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
* TRIO_FEATURE_FD (=0 or =1)
|
||||
*
|
||||
* Define this to 0 to disable compilation of the trio_dprintf() and
|
||||
* trio_dscanf() family of functions, or define to 1 to enable them.
|
||||
*
|
||||
* This may be useful on an embedded platform with no filesystem, or on
|
||||
* a platform that supports file I/O using FILE* but not using raw file
|
||||
* descriptors.
|
||||
*/
|
||||
#if !defined(TRIO_FEATURE_FD)
|
||||
# define TRIO_FEATURE_FD 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
* TRIO_FEATURE_DYNAMICSTRING (=0 or =1)
|
||||
*
|
||||
* Define this to 0 to disable compilation of the trio_aprintf()
|
||||
* family of functions, or define to 1 to enable them.
|
||||
*
|
||||
* If you define both this and TRIO_MINIMAL to 0, then Trio will never
|
||||
* call malloc or free.
|
||||
*/
|
||||
#if !defined(TRIO_FEATURE_DYNAMICSTRING)
|
||||
# define TRIO_FEATURE_DYNAMICSTRING 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
* TRIO_FEATURE_CLOSURE (=0 or =1)
|
||||
*
|
||||
* Define this to 0 to disable compilation of the trio_cprintf() and
|
||||
* trio_cscanf() family of functions, or define to 1 to enable them.
|
||||
*
|
||||
* These functions are rarely needed. This saves a (small) amount of code.
|
||||
*/
|
||||
#if !defined(TRIO_FEATURE_CLOSURE)
|
||||
# define TRIO_FEATURE_CLOSURE 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
* TRIO_FEATURE_ARGFUNC (=0 or =1)
|
||||
*
|
||||
* Define this to 0 to disable compilation of trio_cprintff() and
|
||||
* trio_cscanff() functions and related code (might have a tiny
|
||||
* performance gain), or define to 1 to enable them.
|
||||
*
|
||||
* This functionality is needed only if you have to fetch the arguments using
|
||||
* a pull model instead of passing them all at once (for example if you plan
|
||||
* to plug the library into a script interpreter or validate the types).
|
||||
*
|
||||
* Only the closure family of functions are available with this interface,
|
||||
* because if you need this, you usually provide custom input/output
|
||||
* handling too (and so this forces TRIO_FEATURE_CLOSURE to enabled).
|
||||
*/
|
||||
#if !defined(TRIO_FEATURE_ARGFUNC)
|
||||
# define TRIO_FEATURE_ARGFUNC 1
|
||||
#endif
|
||||
#if TRIO_FEATURE_ARGFUNC
|
||||
# undef TRIO_FEATURE_CLOSURE
|
||||
# define TRIO_FEATURE_CLOSURE 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
* TRIO_FEATURE_ERRORCODE (=0 or =1)
|
||||
*
|
||||
* Define this to 0 to return -1 from the print and scan function on
|
||||
* error, or define to 1 to return a negative number with debugging
|
||||
* information as part of the return code.
|
||||
*
|
||||
* If enabled, the return code will be a negative number, which encodes
|
||||
* an error code and an error location. These can be decoded with the
|
||||
* TRIO_ERROR_CODE and TRIO_ERROR_POSITION macros.
|
||||
*/
|
||||
#if defined(TRIO_ERRORS)
|
||||
# define TRIO_FEATURE_ERRORCODE TRIO_ERRORS
|
||||
#endif
|
||||
#if !defined(TRIO_FEATURE_ERRORCODE)
|
||||
# define TRIO_FEATURE_ERRORCODE 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
* TRIO_FEATURE_STRERR (=0 or =1)
|
||||
*
|
||||
* Define this to 0 if you do not use trio_strerror(), or define to 1 if
|
||||
* you do use it.
|
||||
*
|
||||
* This saves a (small) amount of code.
|
||||
*/
|
||||
#if !defined(TRIO_FEATURE_STRERR)
|
||||
# define TRIO_FEATURE_STRERR 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
* TRIO_FEATURE_FLOAT (=0 or =1)
|
||||
*
|
||||
* Define this to 0 to disable all floating-point support, or define
|
||||
* to 1 to enable it.
|
||||
*
|
||||
* This is useful in restricted embedded platforms that do not support
|
||||
* floating-point. Obviously you cannot use floating-point format
|
||||
* specifiers if you define this.
|
||||
*
|
||||
* Do not compile trionan.c if you disable this.
|
||||
*/
|
||||
#if !defined(TRIO_FEATURE_FLOAT)
|
||||
# define TRIO_FEATURE_FLOAT 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
* TRIO_FEATURE_LOCALE (=0 or =1)
|
||||
*
|
||||
* Define this to 0 to disable customized locale support, or define
|
||||
* to 1 to enable it.
|
||||
*
|
||||
* This saves a (small) amount of code.
|
||||
*/
|
||||
#if !defined(TRIO_FEATURE_LOCALE)
|
||||
# define TRIO_FEATURE_LOCALE 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
* TRIO_MINIMAL
|
||||
*
|
||||
* Define this to disable building the public trionan.h and triostr.h.
|
||||
* If you define this, then you must not compile trionan.c and triostr.c
|
||||
* separately.
|
||||
*/
|
||||
#if defined(TRIO_MINIMAL)
|
||||
# if !defined(TRIO_EMBED_NAN)
|
||||
# define TRIO_EMBED_NAN
|
||||
# endif
|
||||
# if !defined(TRIO_EMBED_STRING)
|
||||
# define TRIO_EMBED_STRING
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* Does not work yet. Do not enable */
|
||||
#ifndef TRIO_FEATURE_WIDECHAR
|
||||
# define TRIO_FEATURE_WIDECHAR 0
|
||||
#endif
|
||||
|
||||
/*************************************************************************
|
||||
* Mapping standards to internal features
|
||||
*/
|
||||
|
||||
#if !defined(TRIO_FEATURE_HEXFLOAT)
|
||||
# define TRIO_FEATURE_HEXFLOAT (TRIO_C99 && TRIO_FEATURE_FLOAT)
|
||||
#endif
|
||||
|
||||
#if !defined(TRIO_FEATURE_LONGDOUBLE)
|
||||
# define TRIO_FEATURE_LONGDOUBLE TRIO_FEATURE_FLOAT
|
||||
#endif
|
||||
|
||||
#if !defined(TRIO_FEATURE_ERRNO)
|
||||
# define TRIO_FEATURE_ERRNO TRIO_GNU
|
||||
#endif
|
||||
|
||||
#if !defined(TRIO_FEATURE_QUAD)
|
||||
# define TRIO_FEATURE_QUAD (TRIO_BSD || TRIO_GNU)
|
||||
#endif
|
||||
|
||||
#if !defined(TRIO_FEATURE_SIZE_T)
|
||||
# define TRIO_FEATURE_SIZE_T TRIO_C99
|
||||
#endif
|
||||
|
||||
#if !defined(TRIO_FEATURE_SIZE_T_UPPER)
|
||||
# define TRIO_FEATURE_SIZE_T_UPPER TRIO_GNU
|
||||
#endif
|
||||
|
||||
#if !defined(TRIO_FEATURE_PTRDIFF_T)
|
||||
# define TRIO_FEATURE_PTRDIFF_T TRIO_C99
|
||||
#endif
|
||||
|
||||
#if !defined(TRIO_FEATURE_INTMAX_T)
|
||||
# define TRIO_FEATURE_INTMAX_T TRIO_C99
|
||||
#endif
|
||||
|
||||
#if !defined(TRIO_FEATURE_FIXED_SIZE)
|
||||
# define TRIO_FEATURE_FIXED_SIZE TRIO_MICROSOFT
|
||||
#endif
|
||||
|
||||
#if !defined(TRIO_FEATURE_POSITIONAL)
|
||||
# define TRIO_FEATURE_POSITIONAL TRIO_UNIX98
|
||||
#endif
|
||||
|
||||
#if !defined(TRIO_FEATURE_USER_DEFINED)
|
||||
# define TRIO_FEATURE_USER_DEFINED TRIO_EXTENSION
|
||||
#endif
|
||||
|
||||
#if !defined(TRIO_FEATURE_BINARY)
|
||||
# define TRIO_FEATURE_BINARY TRIO_EXTENSION
|
||||
#endif
|
||||
|
||||
#if !defined(TRIO_FEATURE_QUOTE)
|
||||
# define TRIO_FEATURE_QUOTE TRIO_EXTENSION
|
||||
#endif
|
||||
|
||||
#if !defined(TRIO_FEATURE_STICKY)
|
||||
# define TRIO_FEATURE_STICKY TRIO_EXTENSION
|
||||
#endif
|
||||
|
||||
#if !defined(TRIO_FEATURE_VARSIZE)
|
||||
# define TRIO_FEATURE_VARSIZE TRIO_EXTENSION
|
||||
#endif
|
||||
|
||||
#if !defined(TRIO_FEATURE_ROUNDING)
|
||||
# define TRIO_FEATURE_ROUNDING TRIO_EXTENSION
|
||||
#endif
|
||||
|
||||
/*************************************************************************
|
||||
* Memory handling
|
||||
*/
|
||||
#ifndef TRIO_MALLOC
|
||||
# define TRIO_MALLOC(n) malloc(n)
|
||||
#endif
|
||||
#ifndef TRIO_REALLOC
|
||||
# define TRIO_REALLOC(x,n) realloc((x),(n))
|
||||
#endif
|
||||
#ifndef TRIO_FREE
|
||||
# define TRIO_FREE(x) free(x)
|
||||
#endif
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
* User-defined specifiers
|
||||
*/
|
||||
|
||||
typedef int (*trio_callback_t) TRIO_PROTO((trio_pointer_t));
|
||||
|
||||
trio_pointer_t trio_register TRIO_PROTO((trio_callback_t callback, const char *name));
|
||||
void trio_unregister TRIO_PROTO((trio_pointer_t handle));
|
||||
|
||||
TRIO_CONST char *trio_get_format TRIO_PROTO((trio_pointer_t ref));
|
||||
TRIO_CONST trio_pointer_t trio_get_argument TRIO_PROTO((trio_pointer_t ref));
|
||||
|
||||
/* Modifiers */
|
||||
int trio_get_width TRIO_PROTO((trio_pointer_t ref));
|
||||
void trio_set_width TRIO_PROTO((trio_pointer_t ref, int width));
|
||||
int trio_get_precision TRIO_PROTO((trio_pointer_t ref));
|
||||
void trio_set_precision TRIO_PROTO((trio_pointer_t ref, int precision));
|
||||
int trio_get_base TRIO_PROTO((trio_pointer_t ref));
|
||||
void trio_set_base TRIO_PROTO((trio_pointer_t ref, int base));
|
||||
int trio_get_padding TRIO_PROTO((trio_pointer_t ref));
|
||||
void trio_set_padding TRIO_PROTO((trio_pointer_t ref, int is_padding));
|
||||
int trio_get_short TRIO_PROTO((trio_pointer_t ref)); /* h */
|
||||
void trio_set_shortshort TRIO_PROTO((trio_pointer_t ref, int is_shortshort));
|
||||
int trio_get_shortshort TRIO_PROTO((trio_pointer_t ref)); /* hh */
|
||||
void trio_set_short TRIO_PROTO((trio_pointer_t ref, int is_short));
|
||||
int trio_get_long TRIO_PROTO((trio_pointer_t ref)); /* l */
|
||||
void trio_set_long TRIO_PROTO((trio_pointer_t ref, int is_long));
|
||||
int trio_get_longlong TRIO_PROTO((trio_pointer_t ref)); /* ll */
|
||||
void trio_set_longlong TRIO_PROTO((trio_pointer_t ref, int is_longlong));
|
||||
int trio_get_longdouble TRIO_PROTO((trio_pointer_t ref)); /* L */
|
||||
void trio_set_longdouble TRIO_PROTO((trio_pointer_t ref, int is_longdouble));
|
||||
int trio_get_alternative TRIO_PROTO((trio_pointer_t ref)); /* # */
|
||||
void trio_set_alternative TRIO_PROTO((trio_pointer_t ref, int is_alternative));
|
||||
int trio_get_alignment TRIO_PROTO((trio_pointer_t ref)); /* - */
|
||||
void trio_set_alignment TRIO_PROTO((trio_pointer_t ref, int is_leftaligned));
|
||||
int trio_get_spacing TRIO_PROTO((trio_pointer_t ref)); /* (space) */
|
||||
void trio_set_spacing TRIO_PROTO((trio_pointer_t ref, int is_space));
|
||||
int trio_get_sign TRIO_PROTO((trio_pointer_t ref)); /* + */
|
||||
void trio_set_sign TRIO_PROTO((trio_pointer_t ref, int is_showsign));
|
||||
#if TRIO_FEATURE_QUOTE
|
||||
int trio_get_quote TRIO_PROTO((trio_pointer_t ref)); /* ' */
|
||||
void trio_set_quote TRIO_PROTO((trio_pointer_t ref, int is_quote));
|
||||
#endif
|
||||
int trio_get_upper TRIO_PROTO((trio_pointer_t ref));
|
||||
void trio_set_upper TRIO_PROTO((trio_pointer_t ref, int is_upper));
|
||||
#if TRIO_FEATURE_INTMAX_T
|
||||
int trio_get_largest TRIO_PROTO((trio_pointer_t ref)); /* j */
|
||||
void trio_set_largest TRIO_PROTO((trio_pointer_t ref, int is_largest));
|
||||
#endif
|
||||
#if TRIO_FEATURE_PTRDIFF_T
|
||||
int trio_get_ptrdiff TRIO_PROTO((trio_pointer_t ref)); /* t */
|
||||
void trio_set_ptrdiff TRIO_PROTO((trio_pointer_t ref, int is_ptrdiff));
|
||||
#endif
|
||||
#if TRIO_FEATURE_SIZE_T
|
||||
int trio_get_size TRIO_PROTO((trio_pointer_t ref)); /* z / Z */
|
||||
void trio_set_size TRIO_PROTO((trio_pointer_t ref, int is_size));
|
||||
#endif
|
||||
|
||||
/* Printing */
|
||||
int trio_print_ref TRIO_PROTO((trio_pointer_t ref, const char *format, ...));
|
||||
int trio_vprint_ref TRIO_PROTO((trio_pointer_t ref, const char *format, va_list args));
|
||||
int trio_printv_ref TRIO_PROTO((trio_pointer_t ref, const char *format, trio_pointer_t *args));
|
||||
|
||||
void trio_print_int TRIO_PROTO((trio_pointer_t ref, int number));
|
||||
void trio_print_uint TRIO_PROTO((trio_pointer_t ref, unsigned int number));
|
||||
/* void trio_print_long TRIO_PROTO((trio_pointer_t ref, long number)); */
|
||||
/* void trio_print_ulong TRIO_PROTO((trio_pointer_t ref, unsigned long number)); */
|
||||
void trio_print_double TRIO_PROTO((trio_pointer_t ref, double number));
|
||||
void trio_print_string TRIO_PROTO((trio_pointer_t ref, TRIO_CONST char *string));
|
||||
void trio_print_pointer TRIO_PROTO((trio_pointer_t ref, trio_pointer_t pointer));
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* TRIO_TRIOP_H */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,681 @@
|
|||
/*************************************************************************
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2001 Bjorn Reese and Daniel Stenberg.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND
|
||||
* CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER.
|
||||
*
|
||||
************************************************************************/
|
||||
|
||||
#ifndef TRIO_TRIOSTR_H
|
||||
#define TRIO_TRIOSTR_H
|
||||
|
||||
/*
|
||||
* Documentation is located in triostr.c
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include "triodef.h"
|
||||
#include "triop.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum {
|
||||
TRIO_HASH_NONE = 0,
|
||||
TRIO_HASH_PLAIN,
|
||||
TRIO_HASH_TWOSIGNED
|
||||
};
|
||||
|
||||
#if !defined(TRIO_PUBLIC_STRING)
|
||||
# if !defined(TRIO_PUBLIC)
|
||||
# define TRIO_PUBLIC
|
||||
# endif
|
||||
# define TRIO_PUBLIC_STRING TRIO_PUBLIC
|
||||
#endif
|
||||
|
||||
/*************************************************************************
|
||||
* Dependencies
|
||||
*/
|
||||
|
||||
#if defined(TRIO_EMBED_STRING)
|
||||
|
||||
/*
|
||||
* The application that triostr is embedded in must define which functions
|
||||
* it uses.
|
||||
*
|
||||
* The following resolves internal dependencies.
|
||||
*/
|
||||
|
||||
# if defined(TRIO_FUNC_XSTRING_SET)
|
||||
# if !defined(TRIO_FUNC_DUPLICATE)
|
||||
# define TRIO_FUNC_DUPLICATE
|
||||
# endif
|
||||
# endif
|
||||
|
||||
# if defined(TRIO_FUNC_DUPLICATE) \
|
||||
|| defined(TRIO_FUNC_DUPLICATE_MAX) \
|
||||
|| defined(TRIO_FUNC_STRING_DUPLICATE) \
|
||||
|| defined(TRIO_FUNC_XSTRING_DUPLICATE)
|
||||
# if !defined(TRIO_FUNC_CREATE)
|
||||
# define TRIO_FUNC_CREATE
|
||||
# endif
|
||||
# if !defined(TRIO_FUNC_COPY_MAX)
|
||||
# define TRIO_FUNC_COPY_MAX
|
||||
# endif
|
||||
# endif
|
||||
|
||||
# if defined(TRIO_FUNC_STRING_CREATE)
|
||||
# if !defined(TRIO_FUNC_STRING_DESTROY)
|
||||
# define TRIO_FUNC_STRING_DESTROY
|
||||
# endif
|
||||
# endif
|
||||
|
||||
# if defined(TRIO_FUNC_STRING_DESTROY) \
|
||||
|| defined(TRIO_FUNC_XSTRING_SET)
|
||||
# if !defined(TRIO_FUNC_DESTROY)
|
||||
# define TRIO_FUNC_DESTROY
|
||||
# endif
|
||||
# endif
|
||||
|
||||
# if defined(TRIO_FUNC_EQUAL_LOCALE) \
|
||||
|| defined(TRIO_FUNC_STRING_EQUAL) \
|
||||
|| defined(TRIO_FUNC_XSTRING_EQUAL)
|
||||
# if !defined(TRIO_FUNC_EQUAL)
|
||||
# define TRIO_FUNC_EQUAL
|
||||
# endif
|
||||
# endif
|
||||
|
||||
# if defined(TRIO_FUNC_EQUAL_CASE) \
|
||||
|| defined(TRIO_FUNC_STRING_EQUAL_CASE) \
|
||||
|| defined(TRIO_FUNC_XSTRING_EQUAL_CASE)
|
||||
# if !defined(TRIO_FUNC_EQUAL_CASE)
|
||||
# define TRIO_FUNC_EQUAL_CASE
|
||||
# endif
|
||||
# endif
|
||||
|
||||
# if defined(TRIO_FUNC_SUBSTRING_MAX) \
|
||||
|| defined(TRIO_FUNC_STRING_EQUAL_MAX) \
|
||||
|| defined(TRIO_FUNC_XSTRING_EQUAL_MAX)
|
||||
# if !defined(TRIO_FUNC_EQUAL_MAX)
|
||||
# define TRIO_FUNC_EQUAL_MAX
|
||||
# endif
|
||||
# endif
|
||||
|
||||
# if defined(TRIO_FUNC_TO_DOUBLE) \
|
||||
|| defined(TRIO_FUNC_TO_FLOAT)
|
||||
# if !defined(TRIO_FUNC_TO_LONG_DOUBLE)
|
||||
# define TRIO_FUNC_TO_LONG_DOUBLE
|
||||
# endif
|
||||
# endif
|
||||
|
||||
# if defined(TRIO_FUNC_STRING_TERMINATE)
|
||||
# if !defined(TRIO_FUNC_XSTRING_APPEND_CHAR)
|
||||
# define TRIO_FUNC_XSTRING_APPEND_CHAR
|
||||
# endif
|
||||
# endif
|
||||
|
||||
# if defined(TRIO_FUNC_XSTRING_APPEND_CHAR)
|
||||
# if !defined(TRIO_FUNC_STRING_SIZE)
|
||||
# define TRIO_FUNC_STRING_SIZE
|
||||
# endif
|
||||
# endif
|
||||
|
||||
#else
|
||||
|
||||
/*
|
||||
* When triostr is not embedded all functions are defined.
|
||||
*/
|
||||
|
||||
# define TRIO_FUNC_APPEND
|
||||
# define TRIO_FUNC_APPEND_MAX
|
||||
# define TRIO_FUNC_CONTAINS
|
||||
# define TRIO_FUNC_COPY
|
||||
# define TRIO_FUNC_COPY_MAX
|
||||
# define TRIO_FUNC_CREATE
|
||||
# define TRIO_FUNC_DESTROY
|
||||
# define TRIO_FUNC_DUPLICATE
|
||||
# define TRIO_FUNC_DUPLICATE_MAX
|
||||
# define TRIO_FUNC_EQUAL
|
||||
# define TRIO_FUNC_EQUAL_CASE
|
||||
# define TRIO_FUNC_EQUAL_CASE_MAX
|
||||
# define TRIO_FUNC_EQUAL_LOCALE
|
||||
# define TRIO_FUNC_EQUAL_MAX
|
||||
# define TRIO_FUNC_ERROR
|
||||
# if !defined(TRIO_PLATFORM_WINCE)
|
||||
# define TRIO_FUNC_FORMAT_DATE_MAX
|
||||
# endif
|
||||
# define TRIO_FUNC_HASH
|
||||
# define TRIO_FUNC_INDEX
|
||||
# define TRIO_FUNC_INDEX_LAST
|
||||
# define TRIO_FUNC_LENGTH
|
||||
# define TRIO_FUNC_LENGTH_MAX
|
||||
# define TRIO_FUNC_LOWER
|
||||
# define TRIO_FUNC_MATCH
|
||||
# define TRIO_FUNC_MATCH_CASE
|
||||
# define TRIO_FUNC_SPAN_FUNCTION
|
||||
# define TRIO_FUNC_SUBSTRING
|
||||
# define TRIO_FUNC_SUBSTRING_MAX
|
||||
# define TRIO_FUNC_TO_DOUBLE
|
||||
# define TRIO_FUNC_TO_FLOAT
|
||||
# define TRIO_FUNC_TO_LONG
|
||||
# define TRIO_FUNC_TO_LONG_DOUBLE
|
||||
# define TRIO_FUNC_TO_LOWER
|
||||
# define TRIO_FUNC_TO_UNSIGNED_LONG
|
||||
# define TRIO_FUNC_TO_UPPER
|
||||
# define TRIO_FUNC_TOKENIZE
|
||||
# define TRIO_FUNC_UPPER
|
||||
|
||||
# define TRIO_FUNC_STRING_APPEND
|
||||
# define TRIO_FUNC_STRING_CONTAINS
|
||||
# define TRIO_FUNC_STRING_COPY
|
||||
# define TRIO_FUNC_STRING_CREATE
|
||||
# define TRIO_FUNC_STRING_DESTROY
|
||||
# define TRIO_FUNC_STRING_DUPLICATE
|
||||
# define TRIO_FUNC_STRING_EQUAL
|
||||
# define TRIO_FUNC_STRING_EQUAL_CASE
|
||||
# define TRIO_FUNC_STRING_EQUAL_CASE_MAX
|
||||
# define TRIO_FUNC_STRING_EQUAL_MAX
|
||||
# define TRIO_FUNC_STRING_EXTRACT
|
||||
# if !defined(TRIO_PLATFORM_WINCE)
|
||||
# define TRIO_FUNC_STRING_FORMAT_DATE_MAX
|
||||
# endif
|
||||
# define TRIO_FUNC_STRING_GET
|
||||
# define TRIO_FUNC_STRING_INDEX
|
||||
# define TRIO_FUNC_STRING_INDEX_LAST
|
||||
# define TRIO_FUNC_STRING_LENGTH
|
||||
# define TRIO_FUNC_STRING_LOWER
|
||||
# define TRIO_FUNC_STRING_MATCH
|
||||
# define TRIO_FUNC_STRING_MATCH_CASE
|
||||
# define TRIO_FUNC_STRING_SIZE
|
||||
# define TRIO_FUNC_STRING_SUBSTRING
|
||||
# define TRIO_FUNC_STRING_TERMINATE
|
||||
# define TRIO_FUNC_STRING_UPPER
|
||||
|
||||
# define TRIO_FUNC_XSTRING_APPEND
|
||||
# define TRIO_FUNC_XSTRING_APPEND_CHAR
|
||||
# define TRIO_FUNC_XSTRING_APPEND_MAX
|
||||
# define TRIO_FUNC_XSTRING_CONTAINS
|
||||
# define TRIO_FUNC_XSTRING_COPY
|
||||
# define TRIO_FUNC_XSTRING_DUPLICATE
|
||||
# define TRIO_FUNC_XSTRING_EQUAL
|
||||
# define TRIO_FUNC_XSTRING_EQUAL_CASE
|
||||
# define TRIO_FUNC_XSTRING_EQUAL_CASE_MAX
|
||||
# define TRIO_FUNC_XSTRING_EQUAL_MAX
|
||||
# define TRIO_FUNC_XSTRING_MATCH
|
||||
# define TRIO_FUNC_XSTRING_MATCH_CASE
|
||||
# define TRIO_FUNC_XSTRING_SET
|
||||
# define TRIO_FUNC_XSTRING_SUBSTRING
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
* String functions
|
||||
*/
|
||||
|
||||
#if defined(TRIO_FUNC_APPEND)
|
||||
TRIO_PUBLIC_STRING int
|
||||
trio_append
|
||||
TRIO_PROTO((char *target, TRIO_CONST char *source));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_APPEND_MAX)
|
||||
TRIO_PUBLIC_STRING int
|
||||
trio_append_max
|
||||
TRIO_PROTO((char *target, size_t max, TRIO_CONST char *source));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_CONTAINS)
|
||||
TRIO_PUBLIC_STRING int
|
||||
trio_contains
|
||||
TRIO_PROTO((TRIO_CONST char *string, TRIO_CONST char *substring));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_COPY)
|
||||
TRIO_PUBLIC_STRING int
|
||||
trio_copy
|
||||
TRIO_PROTO((char *target, TRIO_CONST char *source));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_COPY_MAX)
|
||||
TRIO_PUBLIC_STRING int
|
||||
trio_copy_max
|
||||
TRIO_PROTO((char *target, size_t max, TRIO_CONST char *source));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_CREATE)
|
||||
TRIO_PUBLIC_STRING char *
|
||||
trio_create
|
||||
TRIO_PROTO((size_t size));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_DESTROY)
|
||||
TRIO_PUBLIC_STRING void
|
||||
trio_destroy
|
||||
TRIO_PROTO((char *string));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_DUPLICATE)
|
||||
TRIO_PUBLIC_STRING char *
|
||||
trio_duplicate
|
||||
TRIO_PROTO((TRIO_CONST char *source));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_DUPLICATE_MAX)
|
||||
TRIO_PUBLIC_STRING char *
|
||||
trio_duplicate_max
|
||||
TRIO_PROTO((TRIO_CONST char *source, size_t max));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_EQUAL)
|
||||
TRIO_PUBLIC_STRING int
|
||||
trio_equal
|
||||
TRIO_PROTO((TRIO_CONST char *first, TRIO_CONST char *second));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_EQUAL_CASE)
|
||||
TRIO_PUBLIC_STRING int
|
||||
trio_equal_case
|
||||
TRIO_PROTO((TRIO_CONST char *first, TRIO_CONST char *second));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_EQUAL_CASE_MAX)
|
||||
TRIO_PUBLIC_STRING int
|
||||
trio_equal_case_max
|
||||
TRIO_PROTO((TRIO_CONST char *first, size_t max, TRIO_CONST char *second));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_EQUAL_LOCALE)
|
||||
TRIO_PUBLIC_STRING int
|
||||
trio_equal_locale
|
||||
TRIO_PROTO((TRIO_CONST char *first, TRIO_CONST char *second));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_EQUAL_MAX)
|
||||
TRIO_PUBLIC_STRING int
|
||||
trio_equal_max
|
||||
TRIO_PROTO((TRIO_CONST char *first, size_t max, TRIO_CONST char *second));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_ERROR)
|
||||
TRIO_PUBLIC_STRING TRIO_CONST char *
|
||||
trio_error
|
||||
TRIO_PROTO((int));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_FORMAT_DATE_MAX)
|
||||
TRIO_PUBLIC_STRING size_t
|
||||
trio_format_date_max
|
||||
TRIO_PROTO((char *target, size_t max, TRIO_CONST char *format, TRIO_CONST struct tm *datetime));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_HASH)
|
||||
TRIO_PUBLIC_STRING unsigned long
|
||||
trio_hash
|
||||
TRIO_PROTO((TRIO_CONST char *string, int type));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_INDEX)
|
||||
TRIO_PUBLIC_STRING char *
|
||||
trio_index
|
||||
TRIO_PROTO((TRIO_CONST char *string, int character));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_INDEX_LAST)
|
||||
TRIO_PUBLIC_STRING char *
|
||||
trio_index_last
|
||||
TRIO_PROTO((TRIO_CONST char *string, int character));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_LENGTH)
|
||||
TRIO_PUBLIC_STRING size_t
|
||||
trio_length
|
||||
TRIO_PROTO((TRIO_CONST char *string));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_LENGTH_MAX)
|
||||
TRIO_PUBLIC_STRING size_t
|
||||
trio_length_max
|
||||
TRIO_PROTO((TRIO_CONST char *string, size_t max));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_LOWER)
|
||||
TRIO_PUBLIC_STRING int
|
||||
trio_lower
|
||||
TRIO_PROTO((char *target));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_MATCH)
|
||||
TRIO_PUBLIC_STRING int
|
||||
trio_match
|
||||
TRIO_PROTO((TRIO_CONST char *string, TRIO_CONST char *pattern));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_MATCH_CASE)
|
||||
TRIO_PUBLIC_STRING int
|
||||
trio_match_case
|
||||
TRIO_PROTO((TRIO_CONST char *string, TRIO_CONST char *pattern));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_SPAN_FUNCTION)
|
||||
TRIO_PUBLIC_STRING size_t
|
||||
trio_span_function
|
||||
TRIO_PROTO((char *target, TRIO_CONST char *source, int (*Function) TRIO_PROTO((int))));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_SUBSTRING)
|
||||
TRIO_PUBLIC_STRING char *
|
||||
trio_substring
|
||||
TRIO_PROTO((TRIO_CONST char *string, TRIO_CONST char *substring));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_SUBSTRING_MAX)
|
||||
TRIO_PUBLIC_STRING char *
|
||||
trio_substring_max
|
||||
TRIO_PROTO((TRIO_CONST char *string, size_t max, TRIO_CONST char *substring));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_TO_DOUBLE)
|
||||
TRIO_PUBLIC_STRING double
|
||||
trio_to_double
|
||||
TRIO_PROTO((TRIO_CONST char *source, char **endp));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_TO_FLOAT)
|
||||
TRIO_PUBLIC_STRING float
|
||||
trio_to_float
|
||||
TRIO_PROTO((TRIO_CONST char *source, char **endp));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_TO_LONG)
|
||||
TRIO_PUBLIC_STRING long
|
||||
trio_to_long
|
||||
TRIO_PROTO((TRIO_CONST char *source, char **endp, int base));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_TO_LOWER)
|
||||
TRIO_PUBLIC_STRING int
|
||||
trio_to_lower
|
||||
TRIO_PROTO((int source));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_TO_LONG_DOUBLE)
|
||||
TRIO_PUBLIC_STRING trio_long_double_t
|
||||
trio_to_long_double
|
||||
TRIO_PROTO((TRIO_CONST char *source, char **endp));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_TO_UNSIGNED_LONG)
|
||||
TRIO_PUBLIC_STRING unsigned long
|
||||
trio_to_unsigned_long
|
||||
TRIO_PROTO((TRIO_CONST char *source, char **endp, int base));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_TO_UPPER)
|
||||
TRIO_PUBLIC_STRING int
|
||||
trio_to_upper
|
||||
TRIO_PROTO((int source));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_TOKENIZE)
|
||||
TRIO_PUBLIC_STRING char *
|
||||
trio_tokenize
|
||||
TRIO_PROTO((char *string, TRIO_CONST char *delimiters));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_UPPER)
|
||||
TRIO_PUBLIC_STRING int
|
||||
trio_upper
|
||||
TRIO_PROTO((char *target));
|
||||
#endif
|
||||
|
||||
/*************************************************************************
|
||||
* Dynamic string functions
|
||||
*/
|
||||
|
||||
/*
|
||||
* Opaque type for dynamic strings
|
||||
*/
|
||||
|
||||
typedef struct _trio_string_t trio_string_t;
|
||||
|
||||
#if defined(TRIO_FUNC_STRING_APPEND)
|
||||
TRIO_PUBLIC_STRING int
|
||||
trio_string_append
|
||||
TRIO_PROTO((trio_string_t *self, trio_string_t *other));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_STRING_CONTAINS)
|
||||
TRIO_PUBLIC_STRING int
|
||||
trio_string_contains
|
||||
TRIO_PROTO((trio_string_t *self, trio_string_t *other));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_STRING_COPY)
|
||||
TRIO_PUBLIC_STRING int
|
||||
trio_string_copy
|
||||
TRIO_PROTO((trio_string_t *self, trio_string_t *other));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_STRING_CREATE)
|
||||
TRIO_PUBLIC_STRING trio_string_t *
|
||||
trio_string_create
|
||||
TRIO_PROTO((int initial_size));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_STRING_DESTROY)
|
||||
TRIO_PUBLIC_STRING void
|
||||
trio_string_destroy
|
||||
TRIO_PROTO((trio_string_t *self));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_STRING_DUPLICATE)
|
||||
TRIO_PUBLIC_STRING trio_string_t *
|
||||
trio_string_duplicate
|
||||
TRIO_PROTO((trio_string_t *other));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_STRING_EQUAL)
|
||||
TRIO_PUBLIC_STRING int
|
||||
trio_string_equal
|
||||
TRIO_PROTO((trio_string_t *self, trio_string_t *other));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_STRING_EQUAL_MAX)
|
||||
TRIO_PUBLIC_STRING int
|
||||
trio_string_equal_max
|
||||
TRIO_PROTO((trio_string_t *self, size_t max, trio_string_t *second));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_STRING_EQUAL_CASE)
|
||||
TRIO_PUBLIC_STRING int
|
||||
trio_string_equal_case
|
||||
TRIO_PROTO((trio_string_t *self, trio_string_t *other));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_STRING_EQUAL_CASE_MAX)
|
||||
TRIO_PUBLIC_STRING int
|
||||
trio_string_equal_case_max
|
||||
TRIO_PROTO((trio_string_t *self, size_t max, trio_string_t *other));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_STRING_EXTRACT)
|
||||
TRIO_PUBLIC_STRING char *
|
||||
trio_string_extract
|
||||
TRIO_PROTO((trio_string_t *self));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_STRING_FORMAT_DATE_MAX)
|
||||
TRIO_PUBLIC_STRING size_t
|
||||
trio_string_format_date_max
|
||||
TRIO_PROTO((trio_string_t *self, size_t max, TRIO_CONST char *format, TRIO_CONST struct tm *datetime));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_STRING_GET)
|
||||
TRIO_PUBLIC_STRING char *
|
||||
trio_string_get
|
||||
TRIO_PROTO((trio_string_t *self, int offset));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_STRING_INDEX)
|
||||
TRIO_PUBLIC_STRING char *
|
||||
trio_string_index
|
||||
TRIO_PROTO((trio_string_t *self, int character));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_STRING_INDEX_LAST)
|
||||
TRIO_PUBLIC_STRING char *
|
||||
trio_string_index_last
|
||||
TRIO_PROTO((trio_string_t *self, int character));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_STRING_LENGTH)
|
||||
TRIO_PUBLIC_STRING int
|
||||
trio_string_length
|
||||
TRIO_PROTO((trio_string_t *self));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_STRING_LOWER)
|
||||
TRIO_PUBLIC_STRING int
|
||||
trio_string_lower
|
||||
TRIO_PROTO((trio_string_t *self));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_STRING_MATCH)
|
||||
TRIO_PUBLIC_STRING int
|
||||
trio_string_match
|
||||
TRIO_PROTO((trio_string_t *self, trio_string_t *other));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_STRING_MATCH_CASE)
|
||||
TRIO_PUBLIC_STRING int
|
||||
trio_string_match_case
|
||||
TRIO_PROTO((trio_string_t *self, trio_string_t *other));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_STRING_SIZE)
|
||||
TRIO_PUBLIC_STRING int
|
||||
trio_string_size
|
||||
TRIO_PROTO((trio_string_t *self));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_STRING_SUBSTRING)
|
||||
TRIO_PUBLIC_STRING char *
|
||||
trio_string_substring
|
||||
TRIO_PROTO((trio_string_t *self, trio_string_t *other));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_STRING_TERMINATE)
|
||||
TRIO_PUBLIC_STRING void
|
||||
trio_string_terminate
|
||||
TRIO_PROTO((trio_string_t *self));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_STRING_UPPER)
|
||||
TRIO_PUBLIC_STRING int
|
||||
trio_string_upper
|
||||
TRIO_PROTO((trio_string_t *self));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_XSTRING_APPEND)
|
||||
TRIO_PUBLIC_STRING int
|
||||
trio_xstring_append
|
||||
TRIO_PROTO((trio_string_t *self, TRIO_CONST char *other));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_XSTRING_APPEND_CHAR)
|
||||
TRIO_PUBLIC_STRING int
|
||||
trio_xstring_append_char
|
||||
TRIO_PROTO((trio_string_t *self, char character));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_XSTRING_APPEND_MAX)
|
||||
TRIO_PUBLIC_STRING int
|
||||
trio_xstring_append_max
|
||||
TRIO_PROTO((trio_string_t *self, TRIO_CONST char *other, size_t max));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_XSTRING_CONTAINS)
|
||||
TRIO_PUBLIC_STRING int
|
||||
trio_xstring_contains
|
||||
TRIO_PROTO((trio_string_t *self, TRIO_CONST char *other));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_XSTRING_COPY)
|
||||
TRIO_PUBLIC_STRING int
|
||||
trio_xstring_copy
|
||||
TRIO_PROTO((trio_string_t *self, TRIO_CONST char *other));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_XSTRING_DUPLICATE)
|
||||
TRIO_PUBLIC_STRING trio_string_t *
|
||||
trio_xstring_duplicate
|
||||
TRIO_PROTO((TRIO_CONST char *other));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_XSTRING_EQUAL)
|
||||
TRIO_PUBLIC_STRING int
|
||||
trio_xstring_equal
|
||||
TRIO_PROTO((trio_string_t *self, TRIO_CONST char *other));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_XSTRING_EQUAL_MAX)
|
||||
TRIO_PUBLIC_STRING int
|
||||
trio_xstring_equal_max
|
||||
TRIO_PROTO((trio_string_t *self, size_t max, TRIO_CONST char *other));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_XSTRING_EQUAL_CASE)
|
||||
TRIO_PUBLIC_STRING int
|
||||
trio_xstring_equal_case
|
||||
TRIO_PROTO((trio_string_t *self, TRIO_CONST char *other));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_XSTRING_EQUAL_CASE_MAX)
|
||||
TRIO_PUBLIC_STRING int
|
||||
trio_xstring_equal_case_max
|
||||
TRIO_PROTO((trio_string_t *self, size_t max, TRIO_CONST char *other));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_XSTRING_MATCH)
|
||||
TRIO_PUBLIC_STRING int
|
||||
trio_xstring_match
|
||||
TRIO_PROTO((trio_string_t *self, TRIO_CONST char *other));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_XSTRING_MATCH_CASE)
|
||||
TRIO_PUBLIC_STRING int
|
||||
trio_xstring_match_case
|
||||
TRIO_PROTO((trio_string_t *self, TRIO_CONST char *other));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_XSTRING_SET)
|
||||
TRIO_PUBLIC_STRING void
|
||||
trio_xstring_set
|
||||
TRIO_PROTO((trio_string_t *self, char *buffer));
|
||||
#endif
|
||||
|
||||
#if defined(TRIO_FUNC_XSTRING_SUBSTRING)
|
||||
TRIO_PUBLIC_STRING char *
|
||||
trio_xstring_substring
|
||||
TRIO_PROTO((trio_string_t *self, TRIO_CONST char *other));
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* TRIO_TRIOSTR_H */
|
Loading…
Reference in New Issue