diff --git a/BizHawk.Client.DiscoHawk/BizHawk.Client.DiscoHawk.csproj b/BizHawk.Client.DiscoHawk/BizHawk.Client.DiscoHawk.csproj
index ccd3e21065..47f88a771b 100644
--- a/BizHawk.Client.DiscoHawk/BizHawk.Client.DiscoHawk.csproj
+++ b/BizHawk.Client.DiscoHawk/BizHawk.Client.DiscoHawk.csproj
@@ -100,6 +100,7 @@
MainDiscoForm.cs
+
Form
diff --git a/BizHawk.Client.DiscoHawk/DiscoHawk.cs b/BizHawk.Client.DiscoHawk/DiscoHawk.cs
index 67996cb323..51dd78e8b5 100644
--- a/BizHawk.Client.DiscoHawk/DiscoHawk.cs
+++ b/BizHawk.Client.DiscoHawk/DiscoHawk.cs
@@ -201,6 +201,8 @@ namespace BizHawk.Client.DiscoHawk
else
{
//test stuff...
+ MednadiscTester tester = new MednadiscTester();
+ tester.TestDirectory(@"c:\isos\psx");
}
}
}
diff --git a/BizHawk.Client.DiscoHawk/MednadiscTester.cs b/BizHawk.Client.DiscoHawk/MednadiscTester.cs
new file mode 100644
index 0000000000..fb224be89e
--- /dev/null
+++ b/BizHawk.Client.DiscoHawk/MednadiscTester.cs
@@ -0,0 +1,121 @@
+using System;
+using System.IO;
+using System.Runtime.InteropServices;
+
+using BizHawk.Emulation.DiscSystem;
+
+class MednadiscTester
+{
+ [DllImport("mednadisc.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern IntPtr mednadisc_LoadCD(string path);
+
+ [DllImport("mednadisc.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int mednadisc_ReadSector(IntPtr disc, int lba, byte[] buf2448);
+
+ [DllImport("mednadisc.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void mednadisc_CloseCD(IntPtr disc);
+
+
+ public class QuickSubcodeReader
+ {
+ public QuickSubcodeReader(byte[] buffer)
+ {
+ this.buffer = buffer;
+ }
+
+ public void ReadLBA_SubchannelQ(int offset, ref SubchannelQ sq)
+ {
+ sq.q_status = buffer[offset + 0];
+ sq.q_tno = buffer[offset + 1];
+ sq.q_index = buffer[offset + 2];
+ sq.min.BCDValue = buffer[offset + 3];
+ sq.sec.BCDValue = buffer[offset + 4];
+ sq.frame.BCDValue = buffer[offset + 5];
+ //nothing in byte[6]
+ sq.ap_min.BCDValue = buffer[offset + 7];
+ sq.ap_sec.BCDValue = buffer[offset + 8];
+ sq.ap_frame.BCDValue = buffer[offset + 9];
+
+ //CRC is stored inverted and big endian.. so... do the opposite
+ byte hibyte = (byte)(~buffer[offset + 10]);
+ byte lobyte = (byte)(~buffer[offset + 11]);
+ sq.q_crc = (ushort)((hibyte << 8) | lobyte);
+ }
+
+ byte[] buffer;
+ }
+
+ public void TestDirectory(string dpTarget)
+ {
+ foreach (var fi in new DirectoryInfo(dpTarget).GetFiles())
+ {
+ if (fi.Extension.ToLower() == ".cue") { }
+ else if (fi.Extension.ToLower() == ".ccd") { }
+ else continue;
+
+ NewTest(fi.FullName);
+ }
+ }
+
+ static bool NewTest(string path)
+ {
+ bool ret = false;
+
+ Disc disc;
+ if (Path.GetExtension(path).ToLower() == ".cue") disc = Disc.FromCuePath(path, new CueBinPrefs());
+ else disc = Disc.FromCCDPath(path);
+ IntPtr mednadisc = mednadisc_LoadCD(path);
+
+ //TODO - test leadout a bit, or determine length some superior way
+ //TODO - check length against mednadisc
+
+ int nSectors = (int)(disc.Structure.BinarySize / 2352) - 150;
+ var subbuf = new byte[96];
+ var discbuf = new byte[2352 + 96];
+ var monkeybuf = new byte[2352 + 96];
+ var disc_qbuf = new byte[96];
+ var monkey_qbuf = new byte[96];
+
+ for (int i = 0; i < nSectors; i++)
+ {
+ mednadisc_ReadSector(mednadisc, i, monkeybuf);
+ disc.ReadLBA_2352(i, discbuf, 0);
+ disc.ReadLBA_SectorEntry(i).SubcodeSector.ReadSubcodeDeinterleaved(subbuf, 0);
+ SubcodeUtils.Interleave(subbuf, 0, discbuf, 2352);
+ //remove P
+ for (int q = 2352; q < 2352 + 96; q++)
+ {
+ discbuf[q] &= 0x7F;
+ monkeybuf[q] &= 0x7F;
+ }
+ for (int q = 0; q < 2352 + 96; q++)
+ {
+ if (discbuf[q] != monkeybuf[q])
+ {
+ Console.WriteLine("MISMATCH: " + Path.GetFileName(path));
+
+ //decode Q subchannels for manual investigation
+ SubcodeUtils.Deinterleave(discbuf, 2352, disc_qbuf, 0);
+ var asr = new QuickSubcodeReader(disc_qbuf);
+ SubchannelQ disc_q = new SubchannelQ();
+ asr.ReadLBA_SubchannelQ(12, ref disc_q);
+
+ SubcodeUtils.Deinterleave(monkeybuf, 2352, monkey_qbuf, 0);
+ asr = new QuickSubcodeReader(monkey_qbuf);
+ SubchannelQ monkey_q = new SubchannelQ();
+ asr.ReadLBA_SubchannelQ(12, ref monkey_q);
+
+ goto END;
+ }
+ }
+ }
+
+ ret = true;
+
+ END:
+ disc.Dispose();
+ mednadisc_CloseCD(mednadisc);
+
+ return ret;
+ }
+}
\ No newline at end of file
diff --git a/psx/mednadisc/FileStream.cpp b/psx/mednadisc/FileStream.cpp
new file mode 100644
index 0000000000..61b5d5ca32
--- /dev/null
+++ b/psx/mednadisc/FileStream.cpp
@@ -0,0 +1,349 @@
+/* Mednafen - Multi-system Emulator
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "emuware/emuware.h"
+#include "FileStream.h"
+
+#include
+#include
+#include
+#include
+//#include
+#include
+#include
+
+//work around gettext
+#define _(X) X
+
+#ifdef _MSC_VER
+#include
+#include
+// These are not defined in MSVC version of stat.h
+#define S_IWUSR S_IWRITE
+#define S_IRUSR S_IREAD
+#endif
+
+#ifdef HAVE_MMAP
+#include
+#include
+#include
+#include
+#endif
+
+// Some really bad preprocessor abuse follows to handle platforms that don't have fseeko and ftello...and of course
+// for largefile support on Windows:
+
+#ifndef HAVE_FSEEKO
+ #define fseeko fseek
+#endif
+
+#ifndef HAVE_FTELLO
+ #define ftello ftell
+#endif
+
+#define STRUCT_STAT struct stat
+
+#if SIZEOF_OFF_T == 4
+
+ #ifdef HAVE_FOPEN64
+ #define fopen fopen64
+ #endif
+
+ #ifdef HAVE_FTELLO64
+ #undef ftello
+ #define ftello ftello64
+ #endif
+
+ #ifdef HAVE_FSEEKO64
+ #undef fseeko
+ #define fseeko fseeko64
+ #endif
+
+ #ifdef HAVE_FSTAT64
+ #define fstat fstat64
+ #define stat stat64
+ #undef STRUCT_STAT
+ #define STRUCT_STAT struct stat64
+ #endif
+
+ #ifdef HAVE_FTRUNCATE64
+ #define ftruncate ftruncate64
+ #endif
+#endif
+
+FileStream::FileStream(const std::string& path, const int mode) : OpenedMode(mode), mapping(NULL), mapping_size(0)
+{
+ path_save = path;
+
+ if(mode == MODE_READ)
+ fp = fopen(path.c_str(), "rb");
+ else if(mode == MODE_WRITE)
+ fp = fopen(path.c_str(), "wb");
+ else if(mode == MODE_WRITE_SAFE || mode == MODE_WRITE_INPLACE) // SO ANNOYING
+ {
+ int open_flags = O_WRONLY | O_CREAT;
+
+ if(mode == MODE_WRITE_SAFE)
+ open_flags |= O_EXCL;
+
+ #ifdef O_BINARY
+ open_flags |= O_BINARY;
+ #elif defined(_O_BINARY)
+ open_flags |= _O_BINARY;
+ #endif
+
+ #if defined(S_IRGRP) && defined(S_IROTH)
+ int tmpfd = open(path.c_str(), open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ #else
+ int tmpfd = open(path.c_str(), open_flags, S_IRUSR | S_IWUSR);
+ #endif
+ if(tmpfd == -1)
+ {
+ ErrnoHolder ene(errno);
+
+ throw(MDFN_Error(ene.Errno(), _("Error opening file \"%s\": %s"), path_save.c_str(), ene.StrError()));
+ }
+ fp = fdopen(tmpfd, "wb");
+ }
+ else
+ abort();
+
+ if(!fp)
+ {
+ ErrnoHolder ene(errno);
+
+ throw(MDFN_Error(ene.Errno(), _("Error opening file \"%s\": %s"), path_save.c_str(), ene.StrError()));
+ }
+}
+
+FileStream::~FileStream()
+{
+ try
+ {
+ close();
+ }
+ catch(std::exception &e)
+ {
+ printf(e.what());
+ }
+}
+
+uint64 FileStream::attributes(void)
+{
+ uint64 ret = ATTRIBUTE_SEEKABLE;
+
+ switch(OpenedMode)
+ {
+ case MODE_READ:
+ ret |= ATTRIBUTE_READABLE;
+ break;
+
+ case MODE_WRITE_INPLACE:
+ case MODE_WRITE_SAFE:
+ case MODE_WRITE:
+ ret |= ATTRIBUTE_WRITEABLE;
+ break;
+ }
+
+ return ret;
+}
+
+uint8 *FileStream::map(void) noexcept
+{
+ if(!mapping)
+ {
+#ifdef HAVE_MMAP
+ uint64 length = size();
+ int prot = 0;
+ int flags = 0;
+ void* tptr;
+
+ if(OpenedMode == MODE_READ)
+ {
+ prot |= PROT_READ; // | PROT_EXEC;
+ flags |= MAP_PRIVATE;
+ }
+ else
+ {
+ prot |= PROT_WRITE;
+ prot |= MAP_SHARED;
+ }
+
+ if(length > SIZE_MAX)
+ return(NULL);
+
+ tptr = mmap(NULL, length, prot, flags, fileno(fp), 0);
+ if(tptr != (void*)-1)
+ {
+ mapping = tptr;
+ mapping_size = length;
+
+ #ifdef HAVE_MADVISE
+ // Should probably make this controllable via flag or somesuch.
+ madvise(mapping, mapping_size, MADV_SEQUENTIAL | MADV_WILLNEED);
+ #endif
+ }
+#endif
+ }
+
+ return((uint8*)mapping);
+}
+
+uint64 FileStream::map_size(void) noexcept
+{
+ return mapping_size;
+}
+
+void FileStream::unmap(void) noexcept
+{
+ if(mapping)
+ {
+#ifdef HAVE_MMAP
+ munmap(mapping, mapping_size);
+#endif
+ mapping = NULL;
+ mapping_size = 0;
+ }
+}
+
+
+uint64 FileStream::read(void *data, uint64 count, bool error_on_eos)
+{
+ uint64 read_count;
+
+ clearerr(fp);
+
+ read_count = fread(data, 1, count, fp);
+
+ if(read_count != count)
+ {
+ ErrnoHolder ene(errno);
+
+ if(ferror(fp))
+ throw(MDFN_Error(ene.Errno(), _("Error reading from opened file \"%s\": %s"), path_save.c_str(), ene.StrError()));
+
+ if(error_on_eos)
+ throw(MDFN_Error(0, _("Error reading from opened file \"%s\": %s"), path_save.c_str(), _("Unexpected EOF")));
+ }
+
+ return(read_count);
+}
+
+void FileStream::write(const void *data, uint64 count)
+{
+ if(fwrite(data, 1, count, fp) != count)
+ {
+ ErrnoHolder ene(errno);
+
+ throw(MDFN_Error(ene.Errno(), _("Error writing to opened file \"%s\": %s"), path_save.c_str(), ene.StrError()));
+ }
+}
+
+void FileStream::truncate(uint64 length)
+{
+ //not needed by mednadisc
+ //if(fflush(fp) == EOF || ftruncate(fileno(fp), length) != 0)
+ {
+ ErrnoHolder ene(errno);
+
+ throw(MDFN_Error(ene.Errno(), _("Error truncating opened file \"%s\": %s"), path_save.c_str(), ene.StrError()));
+ }
+}
+
+void FileStream::seek(int64 offset, int whence)
+{
+ if(fseeko(fp, offset, whence) == -1)
+ {
+ ErrnoHolder ene(errno);
+
+ throw(MDFN_Error(ene.Errno(), _("Error seeking in opened file \"%s\": %s"), path_save.c_str(), ene.StrError()));
+ }
+}
+
+void FileStream::flush(void)
+{
+ if(fflush(fp) == EOF)
+ {
+ ErrnoHolder ene(errno);
+
+ throw(MDFN_Error(ene.Errno(), _("Error flushing to opened file \"%s\": %s"), path_save.c_str(), ene.StrError()));
+ }
+}
+
+uint64 FileStream::tell(void)
+{
+ auto offset = ftello(fp);
+
+ if(offset == -1)
+ {
+ ErrnoHolder ene(errno);
+
+ throw(MDFN_Error(ene.Errno(), _("Error getting position in opened file \"%s\": %s"), path_save.c_str(), ene.StrError()));
+ }
+
+ return (std::make_unsigned::type)offset;
+}
+
+uint64 FileStream::size(void)
+{
+ STRUCT_STAT buf;
+
+ if((OpenedMode != MODE_READ && fflush(fp) == EOF) || fstat(fileno(fp), &buf) == -1)
+ {
+ ErrnoHolder ene(errno);
+
+ throw(MDFN_Error(ene.Errno(), _("Error getting the size of opened file \"%s\": %s"), path_save.c_str(), ene.StrError()));
+ }
+
+ return (std::make_unsigned::type)buf.st_size;
+}
+
+void FileStream::close(void)
+{
+ if(fp)
+ {
+ FILE *tmp = fp;
+
+ unmap();
+ fp = NULL;
+
+ if(fclose(tmp) == EOF)
+ {
+ ErrnoHolder ene(errno);
+
+ throw(MDFN_Error(ene.Errno(), _("Error closing opened file \"%s\": %s"), path_save.c_str(), ene.StrError()));
+ }
+ }
+}
+
+int FileStream::get_line(std::string &str)
+{
+ int c;
+
+ str.clear();
+
+ while((c = get_char()) >= 0)
+ {
+ if(c == '\r' || c == '\n' || c == 0)
+ return(c);
+
+ str.push_back(c);
+ }
+
+ return(str.length() ? 256 : -1);
+}
+
diff --git a/psx/mednadisc/FileStream.h b/psx/mednadisc/FileStream.h
new file mode 100644
index 0000000000..3066eb6994
--- /dev/null
+++ b/psx/mednadisc/FileStream.h
@@ -0,0 +1,89 @@
+/* Mednafen - Multi-system Emulator
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __MDFN_FILESTREAM_H
+#define __MDFN_FILESTREAM_H
+
+#include "Stream.h"
+#include "error.h"
+
+#include
+#include
+
+class FileStream : public Stream
+{
+ public:
+
+ enum
+ {
+ MODE_READ = 0,
+ MODE_WRITE,
+ MODE_WRITE_SAFE, // Will throw an exception instead of overwriting an existing file.
+ MODE_WRITE_INPLACE, // Like MODE_WRITE, but won't truncate the file if it already exists.
+ };
+
+ FileStream(const std::string& path, const int mode);
+ virtual ~FileStream() override;
+
+ virtual uint64 attributes(void) override;
+
+ virtual uint8 *map(void) noexcept override;
+ virtual uint64 map_size(void) noexcept override;
+ virtual void unmap(void) noexcept override;
+
+ virtual uint64 read(void *data, uint64 count, bool error_on_eos = true) override;
+ virtual void write(const void *data, uint64 count) override;
+ virtual void truncate(uint64 length) override;
+ virtual void seek(int64 offset, int whence) override;
+ virtual uint64 tell(void) override;
+ virtual uint64 size(void) override;
+ virtual void flush(void) override;
+ virtual void close(void) override;
+
+ virtual int get_line(std::string &str) override;
+
+ INLINE int get_char(void)
+ {
+ int ret;
+
+ errno = 0;
+ ret = fgetc(fp);
+
+ if(MDFN_UNLIKELY(errno != 0))
+ {
+ ErrnoHolder ene(errno);
+ throw(MDFN_Error(ene.Errno(), ("Error reading from opened file \"%s\": %s"), path_save.c_str(), ene.StrError()));
+ }
+ return(ret);
+ }
+
+ private:
+ FileStream & operator=(const FileStream &); // Assignment operator
+ FileStream(const FileStream &); // Copy constructor
+ //FileStream(FileStream &); // Copy constructor
+
+ FILE *fp;
+ std::string path_save;
+ const int OpenedMode;
+
+ void* mapping;
+ uint64 mapping_size;
+};
+
+
+
+#endif
diff --git a/psx/mednadisc/Mednadisc.cpp b/psx/mednadisc/Mednadisc.cpp
new file mode 100644
index 0000000000..ca5c39469d
--- /dev/null
+++ b/psx/mednadisc/Mednadisc.cpp
@@ -0,0 +1,38 @@
+#include "emuware/emuware.h"
+
+#include "Mednadisc.h"
+
+#include "error.h"
+
+#include "cdrom/CDAccess.h"
+#include "cdrom/CDUtility.h"
+#include "cdrom/cdromif.h"
+#include "cdrom/CDAccess_Image.h"
+
+EW_EXPORT void* mednadisc_LoadCD(const char* fname)
+{
+ CDAccess* disc = NULL;
+ try {
+ disc = cdaccess_open_image(fname,false);
+ }
+ catch(MDFN_Error &e) {
+ return NULL;
+ }
+ return disc;
+}
+
+EW_EXPORT int32 mednadisc_ReadSector(CDAccess* disc, int lba, void* buf2448)
+{
+ try {
+ disc->Read_Raw_Sector((uint8*)buf2448,lba);
+ }
+ catch(MDFN_Error &e) {
+ return 0;
+ }
+ return 1;
+}
+
+EW_EXPORT void mednadisc_CloseCD(CDAccess* disc)
+{
+ delete disc;
+}
diff --git a/psx/mednadisc/Mednadisc.h b/psx/mednadisc/Mednadisc.h
new file mode 100644
index 0000000000..e82dd5c4eb
--- /dev/null
+++ b/psx/mednadisc/Mednadisc.h
@@ -0,0 +1,9 @@
+#pragma once
+
+#include "emuware/emuware.h"
+
+class CDAccess;
+
+EW_EXPORT void* mednadisc_LoadCD(const char* fname);
+EW_EXPORT int32 mednadisc_ReadSector(CDAccess* disc, int lba, void* buf2448);
+EW_EXPORT void mednadisc_CloseCD(CDAccess* disc);
\ No newline at end of file
diff --git a/psx/mednadisc/MemoryStream.cpp b/psx/mednadisc/MemoryStream.cpp
new file mode 100644
index 0000000000..4dd932f046
--- /dev/null
+++ b/psx/mednadisc/MemoryStream.cpp
@@ -0,0 +1,330 @@
+/* Mednafen - Multi-system Emulator
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include
+#include "MemoryStream.h"
+#include "math_ops.h"
+#include "error.h"
+
+//work around gettext
+#define _(X) X
+
+/*
+ TODO: Copy and assignment constructor fixes.
+
+ Proper negative position behavior?
+*/
+
+MemoryStream::MemoryStream() : data_buffer(NULL), data_buffer_size(0), data_buffer_alloced(0), position(0)
+{
+ data_buffer_size = 0;
+ data_buffer_alloced = 64;
+ if(!(data_buffer = (uint8*)realloc(data_buffer, data_buffer_alloced)))
+ throw MDFN_Error(ErrnoHolder(errno));
+}
+
+MemoryStream::MemoryStream(uint64 alloc_hint, int alloc_hint_is_size) : data_buffer(NULL), data_buffer_size(0), data_buffer_alloced(0), position(0)
+{
+ if(alloc_hint_is_size != 0)
+ {
+ data_buffer_size = alloc_hint;
+ data_buffer_alloced = alloc_hint;
+
+ if(alloc_hint > SIZE_MAX)
+ throw MDFN_Error(ErrnoHolder(ENOMEM));
+ }
+ else
+ {
+ data_buffer_size = 0;
+ data_buffer_alloced = (alloc_hint > SIZE_MAX) ? SIZE_MAX : alloc_hint;
+ }
+
+ if(!(data_buffer = (uint8*)realloc(data_buffer, data_buffer_alloced)))
+ throw MDFN_Error(ErrnoHolder(errno));
+
+ if(alloc_hint_is_size > 0)
+ memset(data_buffer, 0, data_buffer_size);
+}
+
+MemoryStream::MemoryStream(Stream *stream, uint64 size_limit) : data_buffer(NULL), data_buffer_size(0), data_buffer_alloced(0), position(0)
+{
+ try
+ {
+ if((position = stream->tell()) != 0)
+ stream->seek(0, SEEK_SET);
+
+ void* tp;
+ data_buffer_size = data_buffer_alloced = stream->alloc_and_read(&tp, size_limit);
+ data_buffer = (uint8*)tp;
+ stream->close();
+ }
+ catch(...)
+ {
+ if(data_buffer)
+ {
+ free(data_buffer);
+ data_buffer = NULL;
+ }
+
+ delete stream;
+ throw;
+ }
+ delete stream;
+}
+
+MemoryStream::MemoryStream(const MemoryStream &zs)
+{
+ data_buffer_size = zs.data_buffer_size;
+ data_buffer_alloced = zs.data_buffer_alloced;
+ if(!(data_buffer = (uint8*)malloc(data_buffer_alloced)))
+ throw MDFN_Error(ErrnoHolder(errno));
+
+ memcpy(data_buffer, zs.data_buffer, data_buffer_size);
+
+ position = zs.position;
+}
+
+#if 0
+MemoryStream & MemoryStream::operator=(const MemoryStream &zs)
+{
+ if(this != &zs)
+ {
+ if(data_buffer)
+ {
+ free(data_buffer);
+ data_buffer = NULL;
+ }
+
+ data_buffer_size = zs.data_buffer_size;
+ data_buffer_alloced = zs.data_buffer_alloced;
+
+ if(!(data_buffer = (uint8*)malloc(data_buffer_alloced)))
+ throw MDFN_Error(ErrnoHolder(errno));
+
+ memcpy(data_buffer, zs.data_buffer, data_buffer_size);
+
+ position = zs.position;
+ }
+ return(*this);
+}
+#endif
+
+MemoryStream::~MemoryStream()
+{
+ if(data_buffer)
+ {
+ free(data_buffer);
+ data_buffer = NULL;
+ }
+}
+
+uint64 MemoryStream::attributes(void)
+{
+ return (ATTRIBUTE_READABLE | ATTRIBUTE_WRITEABLE | ATTRIBUTE_SEEKABLE);
+}
+
+
+uint8 *MemoryStream::map(void) noexcept
+{
+ return data_buffer;
+}
+
+uint64 MemoryStream::map_size(void) noexcept
+{
+ return data_buffer_size;
+}
+
+void MemoryStream::unmap(void) noexcept
+{
+
+}
+
+
+INLINE void MemoryStream::grow_if_necessary(uint64 new_required_size, uint64 hole_end)
+{
+ if(new_required_size > data_buffer_size)
+ {
+ const uint64 old_data_buffer_size = data_buffer_size;
+
+ if(new_required_size > data_buffer_alloced)
+ {
+ uint64 new_required_alloced = round_up_pow2(new_required_size);
+ uint8 *new_data_buffer;
+
+ // first condition will happen at new_required_size > (1ULL << 63) due to round_up_pow2() "wrapping".
+ // second condition can occur when running on a 32-bit system.
+ if(new_required_alloced < new_required_size || new_required_alloced > SIZE_MAX)
+ new_required_alloced = SIZE_MAX;
+
+ // If constrained alloc size isn't enough, throw an out-of-memory/address-space type error.
+ if(new_required_alloced < new_required_size)
+ throw MDFN_Error(ErrnoHolder(ENOMEM));
+
+ if(!(new_data_buffer = (uint8*)realloc(data_buffer, new_required_alloced)))
+ throw MDFN_Error(ErrnoHolder(errno));
+
+ //
+ // Assign all in one go after the realloc() so we don't leave our object in an inconsistent state if the realloc() fails.
+ //
+ data_buffer = new_data_buffer;
+ data_buffer_size = new_required_size;
+ data_buffer_alloced = new_required_alloced;
+ }
+ else
+ data_buffer_size = new_required_size;
+
+ if(hole_end > old_data_buffer_size)
+ memset(data_buffer + old_data_buffer_size, 0, hole_end - old_data_buffer_size);
+ }
+}
+
+void MemoryStream::shrink_to_fit(void) noexcept
+{
+ if(data_buffer_alloced > data_buffer_size)
+ {
+ uint8 *new_data_buffer;
+
+ new_data_buffer = (uint8*)realloc(data_buffer, data_buffer_size);
+
+ if(new_data_buffer != NULL)
+ {
+ data_buffer = new_data_buffer;
+ data_buffer_alloced = data_buffer_size;
+ }
+ }
+}
+
+uint64 MemoryStream::read(void *data, uint64 count, bool error_on_eos)
+{
+ //printf("%llu %llu %llu\n", position, count, data_buffer_size);
+
+ if(count > data_buffer_size)
+ {
+ if(error_on_eos)
+ throw MDFN_Error(0, _("Unexpected EOF"));
+
+ count = data_buffer_size;
+ }
+
+ if(position > (data_buffer_size - count))
+ {
+ if(error_on_eos)
+ throw MDFN_Error(0, _("Unexpected EOF"));
+
+ if(data_buffer_size > position)
+ count = data_buffer_size - position;
+ else
+ count = 0;
+ }
+
+ memmove(data, &data_buffer[position], count);
+ position += count;
+
+ return count;
+}
+
+void MemoryStream::write(const void *data, uint64 count)
+{
+ uint64 nrs = position + count;
+
+ if(nrs < position)
+ throw MDFN_Error(ErrnoHolder(EFBIG));
+
+ grow_if_necessary(nrs, position);
+
+ memmove(&data_buffer[position], data, count);
+ position += count;
+}
+
+//
+// Don't add code to reduce the amount of memory allocated(when possible) without providing a
+// per-stream setting to disable that behavior.
+//
+void MemoryStream::truncate(uint64 length)
+{
+ grow_if_necessary(length, length);
+
+ data_buffer_size = length;
+}
+
+void MemoryStream::seek(int64 offset, int whence)
+{
+ uint64 new_position;
+
+ switch(whence)
+ {
+ default:
+ throw MDFN_Error(ErrnoHolder(EINVAL));
+ break;
+
+ case SEEK_SET:
+ new_position = offset;
+ break;
+
+ case SEEK_CUR:
+ new_position = position + offset;
+ break;
+
+ case SEEK_END:
+ new_position = data_buffer_size + offset;
+ break;
+ }
+
+ if(new_position < 0)
+ throw MDFN_Error(ErrnoHolder(EINVAL));
+
+ position = new_position;
+}
+
+uint64 MemoryStream::tell(void)
+{
+ return position;
+}
+
+uint64 MemoryStream::size(void)
+{
+ return data_buffer_size;
+}
+
+void MemoryStream::flush(void)
+{
+
+}
+
+void MemoryStream::close(void)
+{
+
+}
+
+
+int MemoryStream::get_line(std::string &str)
+{
+ str.clear(); // or str.resize(0)??
+
+ while((uint64)position < data_buffer_size)
+ {
+ uint8 c = data_buffer[position++];
+
+ if(c == '\r' || c == '\n' || c == 0)
+ return(c);
+
+ str.push_back(c); // Should be faster than str.append(1, c)
+ }
+
+ return(str.length() ? 256 : -1);
+}
+
diff --git a/psx/mednadisc/MemoryStream.h b/psx/mednadisc/MemoryStream.h
new file mode 100644
index 0000000000..0b245482e5
--- /dev/null
+++ b/psx/mednadisc/MemoryStream.h
@@ -0,0 +1,77 @@
+/* Mednafen - Multi-system Emulator
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ Notes:
+ For performance reasons(like in the state rewinding code), we should try to make sure map()
+ returns a pointer that is aligned to at least what malloc()/realloc() provides.
+ (And maybe forcefully align it to at least 16 bytes in the future)
+*/
+
+#ifndef __MDFN_MEMORYSTREAM_H
+#define __MDFN_MEMORYSTREAM_H
+
+#include "Stream.h"
+
+class MemoryStream : public Stream
+{
+ public:
+
+ MemoryStream();
+ MemoryStream(uint64 alloc_hint, int alloc_hint_is_size = false); // Pass -1 instead of 1 for alloc_hint_is_size to skip initialization of the memory.
+ MemoryStream(Stream *stream, uint64 size_limit = ~(uint64)0);
+ // Will create a MemoryStream equivalent of the contents of "stream", and then "delete stream".
+ // Will only work if stream->tell() == 0, or if "stream" is seekable.
+ // stream will be deleted even if this constructor throws.
+ //
+ // Will throw an exception if the initial size() of the MemoryStream would be greater than size_limit(useful for when passing
+ // in GZFileStream streams).
+
+ MemoryStream(const MemoryStream &zs);
+ MemoryStream & operator=(const MemoryStream &zs);
+
+ virtual ~MemoryStream() override;
+
+ virtual uint64 attributes(void) override;
+
+ virtual uint8 *map(void) noexcept override;
+ virtual uint64 map_size(void) noexcept override;
+ virtual void unmap(void) noexcept override;
+
+ virtual uint64 read(void *data, uint64 count, bool error_on_eos = true) override;
+ virtual void write(const void *data, uint64 count) override;
+ virtual void truncate(uint64 length) override;
+ virtual void seek(int64 offset, int whence) override;
+ virtual uint64 tell(void) override;
+ virtual uint64 size(void) override;
+ virtual void flush(void) override;
+ virtual void close(void) override;
+
+ virtual int get_line(std::string &str) override;
+
+ void shrink_to_fit(void) noexcept; // Minimizes alloced memory.
+
+ private:
+ uint8 *data_buffer;
+ uint64 data_buffer_size;
+ uint64 data_buffer_alloced;
+
+ uint64 position;
+
+ void grow_if_necessary(uint64 new_required_size, uint64 hole_end);
+};
+#endif
diff --git a/psx/mednadisc/Stream.cpp b/psx/mednadisc/Stream.cpp
new file mode 100644
index 0000000000..67389e86d5
--- /dev/null
+++ b/psx/mednadisc/Stream.cpp
@@ -0,0 +1,219 @@
+/* Mednafen - Multi-system Emulator
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include
+#include
+#include
+
+#include "emuware/emuware.h"
+#include "Stream.h"
+#include "error.h"
+
+//work around gettext stuff
+#define _(X) X
+
+
+Stream::Stream()
+{
+
+}
+
+Stream::~Stream()
+{
+
+}
+
+uint64 Stream::read_discard(uint64 count)
+{
+ uint8 buf[1024];
+ uint64 tmp;
+ uint64 ret = 0;
+
+ do
+ {
+ tmp = read(buf, std::min(count, sizeof(buf)), false);
+ count -= tmp;
+ ret += tmp;
+ } while(tmp == sizeof(buf));
+
+ return ret;
+}
+
+uint64 Stream::alloc_and_read(void** data_out, uint64 size_limit)
+{
+ uint8 *data_buffer = NULL;
+ uint64 data_buffer_size = 0;
+ uint64 data_buffer_alloced = 0;
+
+ try
+ {
+ if(attributes() & ATTRIBUTE_SLOW_SIZE)
+ {
+ uint64 rti;
+
+ data_buffer_size = 0;
+ data_buffer_alloced = 65536;
+
+ if(!(data_buffer = (uint8*)realloc(data_buffer, data_buffer_alloced)))
+ throw MDFN_Error(ErrnoHolder(errno));
+
+ while((rti = read(data_buffer + data_buffer_size, data_buffer_alloced - data_buffer_size, false)) > 0)
+ {
+ uint8* new_data_buffer;
+
+ data_buffer_size += rti;
+
+ if(data_buffer_size == data_buffer_alloced)
+ {
+ data_buffer_alloced <<= 1;
+
+ if(data_buffer_alloced > size_limit) // So we can test against our size limit without going far far over it in temporary memory allocations.
+ data_buffer_alloced = size_limit + 1;
+
+ if(data_buffer_size > size_limit)
+ throw MDFN_Error(0, _("Size limit of %llu bytes would be exceeded."), (unsigned long long)size_limit);
+
+ if(!(new_data_buffer = (uint8 *)realloc(data_buffer, data_buffer_alloced)))
+ throw MDFN_Error(ErrnoHolder(errno));
+ data_buffer = new_data_buffer;
+ }
+ else // EOS
+ break;
+ }
+
+ if(data_buffer_alloced > data_buffer_size)
+ {
+ uint8 *new_data_buffer;
+
+ new_data_buffer = (uint8*)realloc(data_buffer, data_buffer_size);
+
+ if(new_data_buffer != NULL)
+ {
+ data_buffer = new_data_buffer;
+ data_buffer_alloced = data_buffer_size;
+ }
+ }
+ }
+ else
+ {
+ data_buffer_size = size();
+ data_buffer_alloced = data_buffer_size;
+
+ if(data_buffer_size > size_limit)
+ throw MDFN_Error(0, _("Size limit of %llu bytes would be exceeded."), (unsigned long long)size_limit);
+
+ if(data_buffer_alloced > SIZE_MAX)
+ throw MDFN_Error(ErrnoHolder(ENOMEM));
+
+ if(!(data_buffer = (uint8*)realloc(data_buffer, data_buffer_alloced)))
+ throw MDFN_Error(ErrnoHolder(errno));
+
+ read(data_buffer, data_buffer_size);
+ }
+ }
+ catch(...)
+ {
+ if(data_buffer)
+ {
+ free(data_buffer);
+ data_buffer = NULL;
+ }
+ throw;
+ }
+
+ *data_out = data_buffer;
+ return data_buffer_size;
+}
+
+uint8* Stream::map(void) noexcept
+{
+ return(NULL);
+}
+
+uint64 Stream::map_size(void) noexcept
+{
+ return 0;
+}
+
+void Stream::unmap(void) noexcept
+{
+
+}
+
+void Stream::put_line(const std::string& str)
+{
+ char l = '\n';
+
+ write(&str[0], str.size());
+ write(&l, sizeof(l));
+}
+
+
+void Stream::print_format(const char *format, ...)
+{
+ char *str = NULL;
+ int rc;
+
+ va_list ap;
+
+ int size = 128;
+ for(;;) {
+ va_list ap;
+ va_start(ap, format);
+ str = (char*)malloc(size);
+ size *= 2;
+ int ret = vsprintf(str, format, ap);
+ va_end(ap);
+ if(ret>=0)
+ break;
+ free(str);
+ }
+
+ if(rc < 0)
+ throw MDFN_Error(0, "Error in trio_vasprintf()");
+ else
+ {
+ try // Bleck
+ {
+ write(str, rc);
+ }
+ catch(...)
+ {
+ free(str);
+ throw;
+ }
+ free(str);
+ }
+}
+
+int Stream::get_line(std::string &str)
+{
+ uint8 c;
+
+ str.clear(); // or str.resize(0)??
+
+ while(read(&c, sizeof(c), false) > 0)
+ {
+ if(c == '\r' || c == '\n' || c == 0)
+ return(c);
+
+ str.push_back(c);
+ }
+
+ return(str.length() ? 256 : -1);
+}
+
diff --git a/psx/mednadisc/Stream.h b/psx/mednadisc/Stream.h
new file mode 100644
index 0000000000..3830f5c6e6
--- /dev/null
+++ b/psx/mednadisc/Stream.h
@@ -0,0 +1,223 @@
+#ifndef __MDFN_STREAM_H
+#define __MDFN_STREAM_H
+
+// TODO?: BufferedStream, no virtual functions, yes inline functions, constructor takes a Stream* argument.
+
+#include "emuware/emuware.h"
+
+#include
+
+#include // For SEEK_* defines, which we will use in Stream out of FORCE OF HABIT.
+#include
+
+#include
+
+class Stream
+{
+ public:
+
+ Stream();
+ virtual ~Stream();
+
+ enum
+ {
+ ATTRIBUTE_READABLE = 1U << 0,
+ ATTRIBUTE_WRITEABLE = 1U << 1,
+ ATTRIBUTE_SEEKABLE = 1U << 2,
+ ATTRIBUTE_SLOW_SEEK = 1U << 3,
+ ATTRIBUTE_SLOW_SIZE = 1U << 4
+ };
+ virtual uint64 attributes(void) = 0;
+
+ virtual uint8 *map(void) noexcept;
+ // Map the entirety of the stream data into the address space of the process, if possible, and return a pointer.
+ // (the returned pointer must be cached, and returned on any subsequent calls to map() without an unmap()
+ // in-between, to facilitate a sort of "feature-testing", to determine if an alternative like "MemoryStream"
+ // should be used).
+ //
+ // If the mapping fails for whatever reason, return NULL rather than throwing an exception.
+ //
+ // For code using this functionality, ensure usage of map_size() instead of size(), unless you're only using a specific derived
+ // class like MemoryStream() where the value returned by size() won't change unexpectedly due to outside factors.
+
+ virtual uint64 map_size(void) noexcept;
+ // The size of the memory mapping area, point to which returned by map().
+ //
+ // Returns 0 on supported, or if no mapping currently exists.
+
+ virtual void unmap(void) noexcept;
+ // Unmap the stream data from the address space. (Possibly invalidating the pointer returned from map()).
+ // (must automatically be called, if necessary, from the destructor).
+ //
+ // If the data can't be "unmapped" as such because it was never mmap()'d or similar in the first place(such as with MemoryStream),
+ // then this will be a nop.
+
+ virtual uint64 read(void *data, uint64 count, bool error_on_eos = true) = 0;
+ virtual void write(const void *data, uint64 count) = 0;
+
+ virtual void truncate(uint64 length) = 0; // Should have ftruncate()-like semantics; but avoid using it to extend files.
+
+ virtual void seek(int64 offset, int whence = SEEK_SET) = 0;
+ inline void rewind(void)
+ {
+ seek(0, SEEK_SET);
+ }
+ virtual uint64 tell(void) = 0;
+ virtual uint64 size(void) = 0; // May implicitly call flush() if the stream is writeable.
+ virtual void flush(void) = 0;
+ virtual void close(void) = 0; // Flushes(in the case of writeable streams) and closes the stream.
+ // Necessary since this operation can fail(running out of disk space, for instance),
+ // and throw an exception in the destructor would be a Bad Idea(TM).
+ //
+ // Manually calling this function isn't strictly necessary, but recommended when the
+ // stream is writeable; it will be called automatically from the destructor, with any
+ // exceptions thrown caught and logged.
+
+ //
+ // Utility functions(TODO):
+ //
+ INLINE uint8 get_u8(void)
+ {
+ uint8 ret;
+
+ read(&ret, sizeof(ret));
+
+ return ret;
+ }
+
+ INLINE void put_u8(uint8 c)
+ {
+ write(&c, sizeof(c));
+ }
+
+
+ template
+ INLINE T get_NE(void)
+ {
+ T ret;
+
+ read(&ret, sizeof(ret));
+
+ return ret;
+ }
+
+
+ template
+ INLINE T get_RE(void)
+ {
+ uint8 tmp[sizeof(T)];
+ union
+ {
+ T ret;
+ uint8 ret_u8[sizeof(T)];
+ };
+
+ read(tmp, sizeof(tmp));
+
+ for(unsigned i = 0; i < sizeof(T); i++)
+ ret_u8[i] = tmp[sizeof(T) - 1 - i];
+
+ return ret;
+ }
+
+ template
+ INLINE void put_NE(T c)
+ {
+ write(&c, sizeof(c));
+ }
+
+ template
+ INLINE void put_RE(T c)
+ {
+ uint8 tmp[sizeof(T)];
+
+ for(unsigned i = 0; i < sizeof(T); i++)
+ tmp[i] = ((uint8 *)&c)[sizeof(T) - 1 - i];
+
+ write(tmp, sizeof(tmp));
+ }
+
+ template
+ INLINE T get_LE(void)
+ {
+ #ifdef LSB_FIRST
+ return get_NE();
+ #else
+ return get_RE();
+ #endif
+ }
+
+ template
+ INLINE void put_LE(T c)
+ {
+ #ifdef LSB_FIRST
+ return put_NE(c);
+ #else
+ return put_RE(c);
+ #endif
+ }
+
+ template
+ INLINE T get_BE(void)
+ {
+ #ifndef LSB_FIRST
+ return get_NE();
+ #else
+ return get_RE();
+ #endif
+ }
+
+ template
+ INLINE void put_BE(T c)
+ {
+ #ifndef LSB_FIRST
+ return put_NE(c);
+ #else
+ return put_RE(c);
+ #endif
+ }
+
+ INLINE void put_string(const char* str)
+ {
+ write(str, strlen(str));
+ }
+
+ // Reads a line into "str", overwriting its contents; returns the line-end char('\n' or '\r' or '\0'), or 256 on EOF and
+ // data has been read into "str", and -1 on EOF when no data has been read into "str".
+ // The line-end char won't be added to "str".
+ // It's up to the caller to handle extraneous empty lines caused by DOS-format text lines(\r\n).
+ // ("str" is passed by reference for the possibility of improved performance by reusing alloced memory for the std::string, though part
+ // of it would be up to the STL implementation).
+ // Implemented as virtual so that a higher-performance version can be implemented if possible(IE with MemoryStream)
+ virtual int get_line(std::string &str);
+
+ virtual void put_line(const std::string& str);
+
+ virtual void print_format(const char *format, ...) MDFN_FORMATSTR(gnu_printf, 2, 3);
+
+#if 0
+ int scanf(const char *format, ...) MDFN_FORMATSTR(gnu_scanf, 2, 3);
+ void put_string(const char *str);
+ void put_string(const std::string &str);
+#endif
+
+ //
+ // Read until end-of-stream(or count), discarding any read data, and returns the amount of data "read".
+ // (Useful for detecting and printing warnings about extra garbage data without needing to call size(),
+ // which can be problematic for some types of Streams).
+ uint64 read_discard(uint64 count = ~(uint64)0);
+
+ //
+ // Reads stream starting at the current stream position(as returned by tell()), into memory allocated with malloc() and realloc(), and
+ // sets *data_out to a pointer to the memory(which the caller will need to free() at some point).
+ //
+ // *data_out is only an output.
+ //
+ // If size_limit is/will be exceeded, an exception will be thrown, and *data_out will not be written to.
+ //
+ // Will return the amount of data read(and the size of the alloced memory).
+ //
+ uint64 alloc_and_read(void** data_out, uint64 size_limit = ~(uint64)0);
+};
+
+#endif
diff --git a/psx/mednadisc/audioreader.cpp b/psx/mednadisc/audioreader.cpp
new file mode 100644
index 0000000000..20d1fe23ca
--- /dev/null
+++ b/psx/mednadisc/audioreader.cpp
@@ -0,0 +1,608 @@
+/* Mednafen - Multi-system Emulator
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+// AR_Open(), and AudioReader, will NOT take "ownership" of the Stream object(IE it won't ever delete it). Though it does assume it has exclusive access
+// to it for as long as the AudioReader object exists.
+
+// Don't allow exceptions to propagate into the vorbis/musepack/etc. libraries, as it could easily leave the state of the library's decoder "object" in an
+// inconsistent state, which would cause all sorts of unfun when we try to destroy it while handling the exception farther up.
+
+#include "../mednafen.h"
+#include "audioreader.h"
+
+#include
+#include
+
+#include "../tremor/ivorbisfile.h"
+#include "../mpcdec/mpcdec.h"
+
+#ifdef HAVE_LIBSNDFILE
+#include
+#endif
+
+#ifdef HAVE_OPUSFILE
+#include "audioreader_opus.h"
+#endif
+
+#include
+#include
+#include
+
+#include "../general.h"
+#include "../endian.h"
+
+AudioReader::AudioReader() : LastReadPos(0)
+{
+
+}
+
+AudioReader::~AudioReader()
+{
+
+}
+
+int64 AudioReader::Read_(int16 *buffer, int64 frames)
+{
+ abort();
+ return(false);
+}
+
+bool AudioReader::Seek_(int64 frame_offset)
+{
+ abort();
+ return(false);
+}
+
+int64 AudioReader::FrameCount(void)
+{
+ abort();
+ return(0);
+}
+
+/*
+**
+**
+**
+**
+**
+**
+**
+**
+**
+*/
+
+class OggVorbisReader : public AudioReader
+{
+ public:
+ OggVorbisReader(Stream *fp);
+ ~OggVorbisReader();
+
+ int64 Read_(int16 *buffer, int64 frames);
+ bool Seek_(int64 frame_offset);
+ int64 FrameCount(void);
+
+ private:
+ OggVorbis_File ovfile;
+ Stream *fw;
+};
+
+
+static size_t iov_read_func(void *ptr, size_t size, size_t nmemb, void *user_data)
+{
+ Stream *fw = (Stream*)user_data;
+
+ if(!size)
+ return(0);
+
+ try
+ {
+ return fw->read(ptr, size * nmemb, false) / size;
+ }
+ catch(...)
+ {
+ return(0);
+ }
+}
+
+static int iov_seek_func(void *user_data, ogg_int64_t offset, int whence)
+{
+ Stream *fw = (Stream*)user_data;
+
+ try
+ {
+ fw->seek(offset, whence);
+ return(0);
+ }
+ catch(...)
+ {
+ return(-1);
+ }
+}
+
+static int iov_close_func(void *user_data)
+{
+ Stream *fw = (Stream*)user_data;
+
+ try
+ {
+ fw->close();
+ return(0);
+ }
+ catch(...)
+ {
+ return EOF;
+ }
+}
+
+static long iov_tell_func(void *user_data)
+{
+ Stream *fw = (Stream*)user_data;
+
+ try
+ {
+ return fw->tell();
+ }
+ catch(...)
+ {
+ return(-1);
+ }
+}
+
+OggVorbisReader::OggVorbisReader(Stream *fp) : fw(fp)
+{
+ ov_callbacks cb;
+
+ memset(&cb, 0, sizeof(cb));
+ cb.read_func = iov_read_func;
+ cb.seek_func = iov_seek_func;
+ cb.close_func = iov_close_func;
+ cb.tell_func = iov_tell_func;
+
+ fp->seek(0, SEEK_SET);
+ if(ov_open_callbacks(fp, &ovfile, NULL, 0, cb))
+ throw(0);
+}
+
+OggVorbisReader::~OggVorbisReader()
+{
+ ov_clear(&ovfile);
+}
+
+int64 OggVorbisReader::Read_(int16 *buffer, int64 frames)
+{
+ uint8 *tw_buf = (uint8 *)buffer;
+ int cursection = 0;
+ long toread = frames * sizeof(int16) * 2;
+
+ while(toread > 0)
+ {
+ long didread = ov_read(&ovfile, (char*)tw_buf, toread, &cursection);
+
+ if(didread == 0)
+ break;
+
+ tw_buf = (uint8 *)tw_buf + didread;
+ toread -= didread;
+ }
+
+ return(frames - toread / sizeof(int16) / 2);
+}
+
+bool OggVorbisReader::Seek_(int64 frame_offset)
+{
+ ov_pcm_seek(&ovfile, frame_offset);
+ return(true);
+}
+
+int64 OggVorbisReader::FrameCount(void)
+{
+ return(ov_pcm_total(&ovfile, -1));
+}
+
+class MPCReader : public AudioReader
+{
+ public:
+ MPCReader(Stream *fp);
+ ~MPCReader();
+
+ int64 Read_(int16 *buffer, int64 frames);
+ bool Seek_(int64 frame_offset);
+ int64 FrameCount(void);
+
+ private:
+ mpc_reader reader;
+ mpc_demux *demux;
+ mpc_streaminfo si;
+
+ MPC_SAMPLE_FORMAT MPCBuffer[MPC_DECODER_BUFFER_LENGTH];
+
+ uint32 MPCBufferIn;
+ uint32 MPCBufferOffs;
+ Stream *fw;
+};
+
+
+/// Reads size bytes of data into buffer at ptr.
+static mpc_int32_t impc_read(mpc_reader *p_reader, void *ptr, mpc_int32_t size)
+{
+ Stream *fw = (Stream*)(p_reader->data);
+
+ try
+ {
+ return fw->read(ptr, size, false);
+ }
+ catch(...)
+ {
+ return(MPC_STATUS_FAIL);
+ }
+}
+
+/// Seeks to byte position offset.
+static mpc_bool_t impc_seek(mpc_reader *p_reader, mpc_int32_t offset)
+{
+ Stream *fw = (Stream*)(p_reader->data);
+
+ try
+ {
+ fw->seek(offset, SEEK_SET);
+ return(MPC_TRUE);
+ }
+ catch(...)
+ {
+ return(MPC_FALSE);
+ }
+}
+
+/// Returns the current byte offset in the stream.
+static mpc_int32_t impc_tell(mpc_reader *p_reader)
+{
+ Stream *fw = (Stream*)(p_reader->data);
+
+ try
+ {
+ return fw->tell();
+ }
+ catch(...)
+ {
+ return(MPC_STATUS_FAIL);
+ }
+}
+
+/// Returns the total length of the source stream, in bytes.
+static mpc_int32_t impc_get_size(mpc_reader *p_reader)
+{
+ Stream *fw = (Stream*)(p_reader->data);
+
+ try
+ {
+ return fw->size();
+ }
+ catch(...)
+ {
+ return(MPC_STATUS_FAIL);
+ }
+}
+
+/// True if the stream is a seekable stream.
+static mpc_bool_t impc_canseek(mpc_reader *p_reader)
+{
+ return(MPC_TRUE);
+}
+
+MPCReader::MPCReader(Stream *fp) : fw(fp)
+{
+ fp->seek(0, SEEK_SET);
+
+ demux = NULL;
+ memset(&si, 0, sizeof(si));
+ memset(MPCBuffer, 0, sizeof(MPCBuffer));
+ MPCBufferOffs = 0;
+ MPCBufferIn = 0;
+
+ memset(&reader, 0, sizeof(reader));
+ reader.read = impc_read;
+ reader.seek = impc_seek;
+ reader.tell = impc_tell;
+ reader.get_size = impc_get_size;
+ reader.canseek = impc_canseek;
+ reader.data = (void*)fp;
+
+ if(!(demux = mpc_demux_init(&reader)))
+ {
+ throw(0);
+ }
+ mpc_demux_get_info(demux, &si);
+
+ if(si.channels != 2)
+ {
+ mpc_demux_exit(demux);
+ demux = NULL;
+ throw MDFN_Error(0, _("MusePack stream has wrong number of channels(%u); the correct number is 2."), si.channels);
+ }
+
+ if(si.sample_freq != 44100)
+ {
+ mpc_demux_exit(demux);
+ demux = NULL;
+ throw MDFN_Error(0, _("MusePack stream has wrong samplerate(%u Hz); the correct samplerate is 44100 Hz."), si.sample_freq);
+ }
+}
+
+MPCReader::~MPCReader()
+{
+ if(demux)
+ {
+ mpc_demux_exit(demux);
+ demux = NULL;
+ }
+}
+
+int64 MPCReader::Read_(int16 *buffer, int64 frames)
+{
+ mpc_status err;
+ int16 *cowbuf = (int16 *)buffer;
+ int32 toread = frames * 2;
+
+ while(toread > 0)
+ {
+ int32 tmplen;
+
+ if(!MPCBufferIn)
+ {
+ mpc_frame_info fi;
+ memset(&fi, 0, sizeof(fi));
+
+ fi.buffer = MPCBuffer;
+ if((err = mpc_demux_decode(demux, &fi)) < 0 || fi.bits == -1)
+ return(frames - toread / 2);
+
+ MPCBufferIn = fi.samples * 2;
+ MPCBufferOffs = 0;
+ }
+
+ tmplen = MPCBufferIn;
+
+ if(tmplen >= toread)
+ tmplen = toread;
+
+ for(int x = 0; x < tmplen; x++)
+ {
+#ifdef MPC_FIXED_POINT
+ int32 samp = MPCBuffer[MPCBufferOffs + x] >> MPC_FIXED_POINT_FRACTPART;
+#else
+ #warning Floating-point MPC decoding path not tested.
+ int32 samp = (int32)(MPCBuffer[MPCBufferOffs + x] * 32767);
+#endif
+ if(samp < -32768)
+ samp = -32768;
+
+ if(samp > 32767)
+ samp = 32767;
+
+ *cowbuf = (int16)samp;
+ cowbuf++;
+ }
+
+ MPCBufferOffs += tmplen;
+ toread -= tmplen;
+ MPCBufferIn -= tmplen;
+ }
+
+ return(frames - toread / 2);
+}
+
+bool MPCReader::Seek_(int64 frame_offset)
+{
+ MPCBufferOffs = 0;
+ MPCBufferIn = 0;
+
+ if(mpc_demux_seek_sample(demux, frame_offset) < 0)
+ return(false);
+
+ return(true);
+}
+
+int64 MPCReader::FrameCount(void)
+{
+ return(mpc_streaminfo_get_length_samples(&si));
+}
+
+/*
+**
+**
+**
+**
+**
+**
+**
+**
+**
+*/
+
+#ifdef HAVE_LIBSNDFILE
+class SFReader : public AudioReader
+{
+ public:
+
+ SFReader(Stream *fp);
+ ~SFReader();
+
+ int64 Read_(int16 *buffer, int64 frames);
+ bool Seek_(int64 frame_offset);
+ int64 FrameCount(void);
+
+ private:
+ SNDFILE *sf;
+ SF_INFO sfinfo;
+ SF_VIRTUAL_IO sfvf;
+
+ Stream *fw;
+};
+
+static sf_count_t isf_get_filelen(void *user_data)
+{
+ Stream *fw = (Stream*)user_data;
+
+ try
+ {
+ return fw->size();
+ }
+ catch(...)
+ {
+ return(-1);
+ }
+}
+
+static sf_count_t isf_seek(sf_count_t offset, int whence, void *user_data)
+{
+ Stream *fw = (Stream*)user_data;
+
+ try
+ {
+ //printf("Seek: offset=%lld, whence=%lld\n", (long long)offset, (long long)whence);
+
+ fw->seek(offset, whence);
+ return fw->tell();
+ }
+ catch(...)
+ {
+ //printf(" SEEK FAILED\n");
+ return(-1);
+ }
+}
+
+static sf_count_t isf_read(void *ptr, sf_count_t count, void *user_data)
+{
+ Stream *fw = (Stream*)user_data;
+
+ try
+ {
+ sf_count_t ret = fw->read(ptr, count, false);
+
+ //printf("Read: count=%lld, ret=%lld\n", (long long)count, (long long)ret);
+
+ return ret;
+ }
+ catch(...)
+ {
+ //printf(" READ FAILED\n");
+ return(0);
+ }
+}
+
+static sf_count_t isf_write(const void *ptr, sf_count_t count, void *user_data)
+{
+ return(0);
+}
+
+static sf_count_t isf_tell(void *user_data)
+{
+ Stream *fw = (Stream*)user_data;
+
+ try
+ {
+ return fw->tell();
+ }
+ catch(...)
+ {
+ return(-1);
+ }
+}
+
+SFReader::SFReader(Stream *fp) : fw(fp)
+{
+ fp->seek(0, SEEK_SET);
+
+ memset(&sfvf, 0, sizeof(sfvf));
+ sfvf.get_filelen = isf_get_filelen;
+ sfvf.seek = isf_seek;
+ sfvf.read = isf_read;
+ sfvf.write = isf_write;
+ sfvf.tell = isf_tell;
+
+ memset(&sfinfo, 0, sizeof(sfinfo));
+ if(!(sf = sf_open_virtual(&sfvf, SFM_READ, &sfinfo, (void*)fp)))
+ throw(0);
+}
+
+SFReader::~SFReader()
+{
+ sf_close(sf);
+}
+
+int64 SFReader::Read_(int16 *buffer, int64 frames)
+{
+ return(sf_read_short(sf, (short*)buffer, frames * 2) / 2);
+}
+
+bool SFReader::Seek_(int64 frame_offset)
+{
+ // FIXME error condition
+ if(sf_seek(sf, frame_offset, SEEK_SET) != frame_offset)
+ return(false);
+ return(true);
+}
+
+int64 SFReader::FrameCount(void)
+{
+ return(sfinfo.frames);
+}
+
+#endif
+
+
+AudioReader *AR_Open(Stream *fp)
+{
+ try
+ {
+ return new MPCReader(fp);
+ }
+ catch(int i)
+ {
+ }
+
+#ifdef HAVE_OPUSFILE
+ try
+ {
+ return new OpusReader(fp);
+ }
+ catch(int i)
+ {
+ }
+#endif
+
+ try
+ {
+ return new OggVorbisReader(fp);
+ }
+ catch(int i)
+ {
+ }
+
+#ifdef HAVE_LIBSNDFILE
+ try
+ {
+ return new SFReader(fp);
+ }
+ catch(int i)
+ {
+ }
+#endif
+
+ return(NULL);
+}
+
diff --git a/psx/mednadisc/audioreader.h b/psx/mednadisc/audioreader.h
new file mode 100644
index 0000000000..014a3e48d3
--- /dev/null
+++ b/psx/mednadisc/audioreader.h
@@ -0,0 +1,43 @@
+#ifndef __MDFN_AUDIOREADER_H
+#define __MDFN_AUDIOREADER_H
+
+#include "../Stream.h"
+
+class AudioReader
+{
+ public:
+ AudioReader();
+ virtual ~AudioReader();
+
+ virtual int64 FrameCount(void);
+ INLINE int64 Read(int64 frame_offset, int16 *buffer, int64 frames)
+ {
+ int64 ret;
+
+ //if(frame_offset >= 0)
+ {
+ if(LastReadPos != frame_offset)
+ {
+ //puts("SEEK");
+ if(!Seek_(frame_offset))
+ return(0);
+ LastReadPos = frame_offset;
+ }
+ }
+ ret = Read_(buffer, frames);
+ LastReadPos += ret;
+ return(ret);
+ }
+
+ private:
+ virtual int64 Read_(int16 *buffer, int64 frames);
+ virtual bool Seek_(int64 frame_offset);
+
+ int64 LastReadPos;
+};
+
+// AR_Open(), and AudioReader, will NOT take "ownership" of the Stream object(IE it won't ever delete it). Though it does assume it has exclusive access
+// to it for as long as the AudioReader object exists.
+AudioReader *AR_Open(Stream *fp);
+
+#endif
diff --git a/psx/mednadisc/bizhawk/mednadisc.filters b/psx/mednadisc/bizhawk/mednadisc.filters
new file mode 100644
index 0000000000..72d05a57b3
--- /dev/null
+++ b/psx/mednadisc/bizhawk/mednadisc.filters
@@ -0,0 +1,248 @@
+
+
+
+
+ {00f73db4-1182-4bf7-b891-66bf860d3742}
+
+
+ {f69cc8f2-7480-44d6-9a32-9dca789d2bf6}
+
+
+ {57a8e6ec-9225-410d-b38f-ba209abae070}
+
+
+ {76abb796-5411-4d33-b3e0-f1f3873f138e}
+
+
+ {cb700979-4dce-4b10-8521-3ab71a313271}
+
+
+ {d1f71901-17a5-441a-8b4f-f7da34a057c1}
+
+
+
+
+ psx
+
+
+ psx
+
+
+ psx
+
+
+ psx
+
+
+ psx
+
+
+
+ psx
+
+
+ psx
+
+
+ psx
+
+
+ psx
+
+
+ psx
+
+
+ psx
+
+
+ psx
+
+
+
+ psx\input
+
+
+ psx\input
+
+
+ psx\input
+
+
+ psx\input
+
+
+ psx\input
+
+
+ psx\input
+
+
+ psx\input
+
+
+ psx\input
+
+
+ psx\input
+
+
+
+ emuware
+
+
+ video
+
+
+ video
+
+
+ emuware
+
+
+ psx
+
+
+ psx
+
+
+ psx
+
+
+ psx
+
+
+ cdrom
+
+
+
+
+ psx
+
+
+ psx
+
+
+ emuware
+
+
+ psx
+
+
+ psx
+
+
+
+
+
+ psx
+
+
+
+
+
+ psx
+
+
+ psx
+
+
+ psx
+
+
+ psx
+
+
+ psx
+
+
+ psx
+
+
+ psx
+
+
+ psx\input
+
+
+ psx\input
+
+
+ psx\input
+
+
+ psx\input
+
+
+ psx\input
+
+
+ psx\input
+
+
+ psx\input
+
+
+ psx\input
+
+
+ psx\input
+
+
+ emuware\msvc
+
+
+ emuware\msvc
+
+
+ video
+
+
+ video
+
+
+ emuware
+
+
+ cdrom
+
+
+ psx
+
+
+ psx
+
+
+ cdrom
+
+
+
+
+ psx
+
+
+ psx
+
+
+ psx
+
+
+ psx
+
+
+ psx
+
+
+ psx
+
+
+ psx
+
+
+ psx
+
+
+ psx
+
+
+
\ No newline at end of file
diff --git a/psx/mednadisc/bizhawk/mednadisc.sln b/psx/mednadisc/bizhawk/mednadisc.sln
new file mode 100644
index 0000000000..948842cfcc
--- /dev/null
+++ b/psx/mednadisc/bizhawk/mednadisc.sln
@@ -0,0 +1,24 @@
+
+Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual Studio 2010
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mednadisc", "mednadisc.vcxproj", "{5F35CAFC-6208-4FBE-AD17-0E69BA3F70EC}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Release|Win32 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {5F35CAFC-6208-4FBE-AD17-0E69BA3F70EC}.Debug|Win32.ActiveCfg = Debug|Win32
+ {5F35CAFC-6208-4FBE-AD17-0E69BA3F70EC}.Debug|Win32.Build.0 = Debug|Win32
+ {5F35CAFC-6208-4FBE-AD17-0E69BA3F70EC}.Release|Win32.ActiveCfg = Release|Win32
+ {5F35CAFC-6208-4FBE-AD17-0E69BA3F70EC}.Release|Win32.Build.0 = Release|Win32
+ {5A0DAC84-1170-4B1A-B9A9-F566A1D97790}.Debug|Win32.ActiveCfg = Debug|Win32
+ {5A0DAC84-1170-4B1A-B9A9-F566A1D97790}.Debug|Win32.Build.0 = Debug|Win32
+ {5A0DAC84-1170-4B1A-B9A9-F566A1D97790}.Release|Win32.ActiveCfg = Release|Win32
+ {5A0DAC84-1170-4B1A-B9A9-F566A1D97790}.Release|Win32.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/psx/mednadisc/bizhawk/mednadisc.vcxproj b/psx/mednadisc/bizhawk/mednadisc.vcxproj
new file mode 100644
index 0000000000..1dfd3512b7
--- /dev/null
+++ b/psx/mednadisc/bizhawk/mednadisc.vcxproj
@@ -0,0 +1,134 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {5F35CAFC-6208-4FBE-AD17-0E69BA3F70EC}
+ Win32Proj
+ mednadisc
+
+
+
+ DynamicLibrary
+ true
+ Unicode
+
+
+ DynamicLibrary
+ false
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ $(ProjectDir)\..\..\..\output\dll\
+
+
+ false
+ $(ProjectDir)\..\..\..\output\dll\
+
+
+
+ NotUsing
+ Level3
+ Disabled
+ EW_EXPORT;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_WINDOWS;_USRDLL;OCTOSHOCK_EXPORTS;%(PreprocessorDefinitions)
+ ../emuware/msvc;..
+
+
+
+
+ true
+ false
+
+
+ Windows
+ true
+
+
+
+
+ Level3
+ NotUsing
+ MaxSpeed
+ true
+ true
+ _CRT_SECURE_NO_WARNINGS;EW_EXPORT;WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)
+
+
+
+
+ true
+ ../emuware/msvc;..
+
+
+ Windows
+ true
+ true
+ true
+
+
+
+
+
+
\ No newline at end of file
diff --git a/psx/mednadisc/bizhawk/mednadisc.vcxproj.filters b/psx/mednadisc/bizhawk/mednadisc.vcxproj.filters
new file mode 100644
index 0000000000..9aaf63d447
--- /dev/null
+++ b/psx/mednadisc/bizhawk/mednadisc.vcxproj.filters
@@ -0,0 +1,104 @@
+
+
+
+
+ {a3ffd332-9644-473f-b3b6-d31b08be5256}
+
+
+ {99e57b88-966c-4695-8f5c-1db6345b1b26}
+
+
+ {798fa5bd-6381-487a-99d2-35a15a6da439}
+
+
+
+
+ cdrom
+
+
+ cdrom
+
+
+ cdrom
+
+
+ cdrom
+
+
+ cdrom
+
+
+ cdrom
+
+
+ cdrom
+
+
+ cdrom
+
+
+ cdrom
+
+
+ cdrom
+
+
+
+
+
+
+
+ string
+
+
+
+ cdrom
+
+
+
+
+
+ cdrom
+
+
+ cdrom
+
+
+ cdrom
+
+
+ cdrom
+
+
+ cdrom
+
+
+ cdrom
+
+
+ cdrom
+
+
+ cdrom
+
+
+ cdrom
+
+
+ emuware
+
+
+
+
+
+
+
+ string
+
+
+
+ cdrom
+
+
+
+
\ No newline at end of file
diff --git a/psx/mednadisc/cdrom/CDAccess.cpp b/psx/mednadisc/cdrom/CDAccess.cpp
new file mode 100644
index 0000000000..641fc4c4ae
--- /dev/null
+++ b/psx/mednadisc/cdrom/CDAccess.cpp
@@ -0,0 +1,58 @@
+/* Mednafen - Multi-system Emulator
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "emuware/emuware.h"
+#include "CDAccess.h"
+#include "CDAccess_Image.h"
+#include "CDAccess_CCD.h"
+
+#ifdef HAVE_LIBCDIO
+#include "CDAccess_Physical.h"
+#endif
+
+using namespace CDUtility;
+
+CDAccess::CDAccess()
+{
+
+}
+
+CDAccess::~CDAccess()
+{
+
+}
+
+CDAccess *cdaccess_open_image(const std::string& path, bool image_memcache)
+{
+ CDAccess *ret = NULL;
+
+ if(path.size() >= 4 && !strcasecmp(path.c_str() + path.size() - 4, ".ccd"))
+ ret = new CDAccess_CCD(path, image_memcache);
+ else
+ ret = new CDAccess_Image(path, image_memcache);
+
+ return ret;
+}
+
+CDAccess *cdaccess_open_phys(const std::string& devicename)
+{
+ #ifdef HAVE_LIBCDIO
+ return new CDAccess_Physical(devicename);
+ #else
+ throw MDFN_Error(0, ("Physical CD access support not compiled in."));
+ #endif
+}
diff --git a/psx/mednadisc/cdrom/CDAccess.h b/psx/mednadisc/cdrom/CDAccess.h
new file mode 100644
index 0000000000..667c6b8b66
--- /dev/null
+++ b/psx/mednadisc/cdrom/CDAccess.h
@@ -0,0 +1,32 @@
+#ifndef __MDFN_CDROMFILE_H
+#define __MDFN_CDROMFILE_H
+
+#include
+#include
+
+#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
diff --git a/psx/mednadisc/cdrom/CDAccess_CCD.cpp b/psx/mednadisc/cdrom/CDAccess_CCD.cpp
new file mode 100644
index 0000000000..6817a20d03
--- /dev/null
+++ b/psx/mednadisc/cdrom/CDAccess_CCD.cpp
@@ -0,0 +1,435 @@
+/* Mednafen - Multi-system Emulator
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "emuware/emuware.h"
+#include "../general.h"
+#include "../string/trim.h"
+#include "CDAccess_CCD.h"
+//#include
+
+//wrapper to repair gettext stuff
+#define _(X) X
+
+#include
+#include
+#include