Basic CD image loading
This commit is contained in:
parent
53e755aa68
commit
ad652c47ed
|
@ -0,0 +1,119 @@
|
||||||
|
#include "cd_image.h"
|
||||||
|
#include "YBaseLib/ByteStream.h"
|
||||||
|
#include "YBaseLib/Log.h"
|
||||||
|
Log_SetChannel(CDImage);
|
||||||
|
|
||||||
|
CDImage::CDImage() = default;
|
||||||
|
|
||||||
|
CDImage::~CDImage()
|
||||||
|
{
|
||||||
|
if (m_data_file)
|
||||||
|
m_data_file->Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr u64 CDImage::MSFToLBA(u32 minute, u32 second, u32 frame)
|
||||||
|
{
|
||||||
|
return ZeroExtend64(minute) * FRAMES_PER_MINUTE + ZeroExtend64(second) * FRAMES_PER_SECOND + ZeroExtend64(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr void CDImage::LBAToMSF(u64 lba, u32* minute, u32* second, u32* frame)
|
||||||
|
{
|
||||||
|
const u32 offset = lba % FRAMES_PER_MINUTE;
|
||||||
|
*minute = Truncate32(lba / FRAMES_PER_MINUTE);
|
||||||
|
*second = Truncate32(offset / FRAMES_PER_SECOND);
|
||||||
|
*frame = Truncate32(offset % FRAMES_PER_SECOND);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CDImage::Open(const char* path)
|
||||||
|
{
|
||||||
|
Assert(!m_data_file);
|
||||||
|
|
||||||
|
if (!ByteStream_OpenFileStream(path, BYTESTREAM_OPEN_READ | BYTESTREAM_OPEN_SEEKABLE, &m_data_file))
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("Failed to open '%s'", path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_lba_count = m_data_file->GetSize() / RAW_SECTOR_SIZE;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CDImage::Seek(u64 lba)
|
||||||
|
{
|
||||||
|
if (lba >= m_lba_count)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!m_data_file->SeekAbsolute(lba * RAW_SECTOR_SIZE))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_current_lba = lba;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CDImage::Seek(u32 minute, u32 second, u32 frame)
|
||||||
|
{
|
||||||
|
return Seek(MSFToLBA(minute, second, frame));
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 CDImage::Read(ReadMode read_mode, u64 lba, u32 sector_count, void* buffer)
|
||||||
|
{
|
||||||
|
if (!Seek(lba))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return Read(read_mode, sector_count, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 CDImage::Read(ReadMode read_mode, u32 minute, u32 second, u32 frame, u32 sector_count, void* buffer)
|
||||||
|
{
|
||||||
|
if (!Seek(minute, second, frame))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return Read(read_mode, sector_count, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 CDImage::Read(ReadMode read_mode, u32 sector_count, void* buffer)
|
||||||
|
{
|
||||||
|
char* buffer_ptr = static_cast<char*>(buffer);
|
||||||
|
u32 sectors_read = 0;
|
||||||
|
for (; sectors_read < sector_count; sectors_read++)
|
||||||
|
{
|
||||||
|
if (m_current_lba == m_lba_count)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// get raw sector
|
||||||
|
char raw_sector[RAW_SECTOR_SIZE];
|
||||||
|
if (!m_data_file->Read2(raw_sector, RAW_SECTOR_SIZE))
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("Read of LBA %llu failed", m_current_lba);
|
||||||
|
m_data_file->SeekAbsolute(m_current_lba * RAW_SECTOR_SIZE);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (read_mode)
|
||||||
|
{
|
||||||
|
case ReadMode::DataOnly:
|
||||||
|
std::memcpy(buffer_ptr, raw_sector + SECTOR_SYNC_SIZE, DATA_SECTOR_SIZE);
|
||||||
|
buffer_ptr += DATA_SECTOR_SIZE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ReadMode::RawNoSync:
|
||||||
|
std::memcpy(buffer_ptr, raw_sector + SECTOR_SYNC_SIZE, RAW_SECTOR_SIZE - SECTOR_SYNC_SIZE);
|
||||||
|
buffer_ptr += RAW_SECTOR_SIZE - SECTOR_SYNC_SIZE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ReadMode::RawSector:
|
||||||
|
std::memcpy(buffer_ptr, raw_sector, RAW_SECTOR_SIZE);
|
||||||
|
buffer_ptr += RAW_SECTOR_SIZE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
UnreachableCode();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_current_lba++;
|
||||||
|
sectors_read++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sectors_read;
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
#pragma once
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
class ByteStream;
|
||||||
|
|
||||||
|
class CDImage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CDImage();
|
||||||
|
~CDImage();
|
||||||
|
|
||||||
|
enum class ReadMode : u32
|
||||||
|
{
|
||||||
|
DataOnly, // 2048 bytes per sector.
|
||||||
|
RawSector, // 2352 bytes per sector.
|
||||||
|
RawNoSync, // 2340 bytes per sector.
|
||||||
|
};
|
||||||
|
|
||||||
|
// Conversion helpers.
|
||||||
|
static constexpr u64 MSFToLBA(u32 minute, u32 second, u32 frame);
|
||||||
|
static constexpr void LBAToMSF(u64 lba, u32* minute, u32* second, u32* frame);
|
||||||
|
|
||||||
|
// Accessors.
|
||||||
|
u64 GetCurrentLBA() const { return m_current_lba; }
|
||||||
|
u64 GetLBACount() const { return m_lba_count; }
|
||||||
|
|
||||||
|
bool Open(const char* path);
|
||||||
|
|
||||||
|
// Seek to data LBA.
|
||||||
|
bool Seek(u64 lba);
|
||||||
|
|
||||||
|
// Seek to audio timestamp (MSF).
|
||||||
|
bool Seek(u32 minute, u32 second, u32 frame);
|
||||||
|
|
||||||
|
// Seek and read at the same time.
|
||||||
|
u32 Read(ReadMode read_mode, u64 lba, u32 sector_count, void* buffer);
|
||||||
|
u32 Read(ReadMode read_mode, u32 minute, u32 second, u32 frame, u32 sector_count, void* buffer);
|
||||||
|
|
||||||
|
// Read from the current LBA. Returns the number of sectors read.
|
||||||
|
u32 Read(ReadMode read_mode, u32 sector_count, void* buffer);
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum : u32
|
||||||
|
{
|
||||||
|
RAW_SECTOR_SIZE = 2352,
|
||||||
|
DATA_SECTOR_SIZE = 2048,
|
||||||
|
SECTOR_SYNC_SIZE = 12,
|
||||||
|
FRAMES_PER_SECOND = 75, // "sectors"
|
||||||
|
SECONDS_PER_MINUTE = 60,
|
||||||
|
FRAMES_PER_MINUTE = FRAMES_PER_SECOND * SECONDS_PER_MINUTE,
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: Multiple data files from cue sheet
|
||||||
|
ByteStream* m_data_file = nullptr;
|
||||||
|
|
||||||
|
// Current LBA/total LBAs.
|
||||||
|
u64 m_current_lba = 0;
|
||||||
|
u64 m_lba_count = 0;
|
||||||
|
};
|
|
@ -37,6 +37,7 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="audio.h" />
|
<ClInclude Include="audio.h" />
|
||||||
<ClInclude Include="bitfield.h" />
|
<ClInclude Include="bitfield.h" />
|
||||||
|
<ClInclude Include="cd_image.h" />
|
||||||
<ClInclude Include="display.h" />
|
<ClInclude Include="display.h" />
|
||||||
<ClInclude Include="display_renderer_d3d.h" />
|
<ClInclude Include="display_renderer_d3d.h" />
|
||||||
<ClInclude Include="display_renderer.h" />
|
<ClInclude Include="display_renderer.h" />
|
||||||
|
@ -57,6 +58,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="audio.cpp" />
|
<ClCompile Include="audio.cpp" />
|
||||||
|
<ClCompile Include="cd_image.cpp" />
|
||||||
<ClCompile Include="display.cpp" />
|
<ClCompile Include="display.cpp" />
|
||||||
<ClCompile Include="display_renderer_d3d.cpp" />
|
<ClCompile Include="display_renderer_d3d.cpp" />
|
||||||
<ClCompile Include="display_renderer.cpp" />
|
<ClCompile Include="display_renderer.cpp" />
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
<ClInclude Include="gl_program.h" />
|
<ClInclude Include="gl_program.h" />
|
||||||
<ClInclude Include="gl_texture.h" />
|
<ClInclude Include="gl_texture.h" />
|
||||||
<ClInclude Include="fifo_queue.h" />
|
<ClInclude Include="fifo_queue.h" />
|
||||||
|
<ClInclude Include="cd_image.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="hdd_image.cpp" />
|
<ClCompile Include="hdd_image.cpp" />
|
||||||
|
@ -36,6 +37,7 @@
|
||||||
<ClCompile Include="state_wrapper.cpp" />
|
<ClCompile Include="state_wrapper.cpp" />
|
||||||
<ClCompile Include="gl_program.cpp" />
|
<ClCompile Include="gl_program.cpp" />
|
||||||
<ClCompile Include="gl_texture.cpp" />
|
<ClCompile Include="gl_texture.cpp" />
|
||||||
|
<ClCompile Include="cd_image.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Natvis Include="bitfield.natvis" />
|
<Natvis Include="bitfield.natvis" />
|
||||||
|
|
|
@ -67,7 +67,7 @@ static void APIENTRY GLDebugCallback(GLenum source, GLenum type, GLuint id, GLen
|
||||||
Log_InfoPrintf(message);
|
Log_InfoPrintf(message);
|
||||||
break;
|
break;
|
||||||
case GL_DEBUG_SEVERITY_NOTIFICATION:
|
case GL_DEBUG_SEVERITY_NOTIFICATION:
|
||||||
Log_DebugPrint(message);
|
// Log_DebugPrint(message);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "cdrom.h"
|
#include "cdrom.h"
|
||||||
#include "YBaseLib/Log.h"
|
#include "YBaseLib/Log.h"
|
||||||
|
#include "common/cd_image.h"
|
||||||
#include "common/state_wrapper.h"
|
#include "common/state_wrapper.h"
|
||||||
#include "interrupt_controller.h"
|
#include "interrupt_controller.h"
|
||||||
Log_SetChannel(CDROM);
|
Log_SetChannel(CDROM);
|
||||||
|
@ -43,6 +44,34 @@ bool CDROM::DoState(StateWrapper& sw)
|
||||||
return !sw.HasError();
|
return !sw.HasError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CDROM::InsertMedia(const char* filename)
|
||||||
|
{
|
||||||
|
auto media = std::make_unique<CDImage>();
|
||||||
|
if (!media->Open(filename))
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("Failed to open media at '%s'", filename);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HasMedia())
|
||||||
|
RemoveMedia();
|
||||||
|
|
||||||
|
m_media = std::move(media);
|
||||||
|
m_secondary_status.shell_open = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDROM::RemoveMedia()
|
||||||
|
{
|
||||||
|
if (!m_media)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// TODO: Error while reading?
|
||||||
|
Log_InfoPrintf("Removing CD...");
|
||||||
|
m_media.reset();
|
||||||
|
m_secondary_status.shell_open = true;
|
||||||
|
}
|
||||||
|
|
||||||
u8 CDROM::ReadRegister(u32 offset)
|
u8 CDROM::ReadRegister(u32 offset)
|
||||||
{
|
{
|
||||||
switch (offset)
|
switch (offset)
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "common/bitfield.h"
|
#include "common/bitfield.h"
|
||||||
#include "common/fifo_queue.h"
|
#include "common/fifo_queue.h"
|
||||||
|
|
||||||
|
class CDImage;
|
||||||
class StateWrapper;
|
class StateWrapper;
|
||||||
|
|
||||||
class DMA;
|
class DMA;
|
||||||
|
@ -18,6 +19,10 @@ public:
|
||||||
void Reset();
|
void Reset();
|
||||||
bool DoState(StateWrapper& sw);
|
bool DoState(StateWrapper& sw);
|
||||||
|
|
||||||
|
bool HasMedia() const { return static_cast<bool>(m_media); }
|
||||||
|
bool InsertMedia(const char* filename);
|
||||||
|
void RemoveMedia();
|
||||||
|
|
||||||
// I/O
|
// I/O
|
||||||
u8 ReadRegister(u32 offset);
|
u8 ReadRegister(u32 offset);
|
||||||
void WriteRegister(u32 offset, u8 value);
|
void WriteRegister(u32 offset, u8 value);
|
||||||
|
@ -84,6 +89,7 @@ private:
|
||||||
|
|
||||||
DMA* m_dma;
|
DMA* m_dma;
|
||||||
InterruptController* m_interrupt_controller;
|
InterruptController* m_interrupt_controller;
|
||||||
|
std::unique_ptr<CDImage> m_media;
|
||||||
|
|
||||||
enum class State : u32
|
enum class State : u32
|
||||||
{
|
{
|
||||||
|
|
|
@ -184,6 +184,9 @@ void Core::Branch(u32 target)
|
||||||
u32 Core::GetExceptionVector(Exception excode) const
|
u32 Core::GetExceptionVector(Exception excode) const
|
||||||
{
|
{
|
||||||
const u32 base = m_cop0_regs.sr.BEV ? UINT32_C(0xbfc00100) : UINT32_C(0x80000000);
|
const u32 base = m_cop0_regs.sr.BEV ? UINT32_C(0xbfc00100) : UINT32_C(0x80000000);
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
// apparently this isn't correct...
|
||||||
switch (excode)
|
switch (excode)
|
||||||
{
|
{
|
||||||
case Exception::BP:
|
case Exception::BP:
|
||||||
|
@ -192,6 +195,9 @@ u32 Core::GetExceptionVector(Exception excode) const
|
||||||
default:
|
default:
|
||||||
return base | UINT32_C(0x00000080);
|
return base | UINT32_C(0x00000080);
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
return base | UINT32_C(0x00000080);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::RaiseException(Exception excode, u8 coprocessor /* = 0 */)
|
void Core::RaiseException(Exception excode, u8 coprocessor /* = 0 */)
|
||||||
|
@ -344,14 +350,15 @@ bool Core::FetchInstruction()
|
||||||
{
|
{
|
||||||
m_regs.pc = m_regs.npc;
|
m_regs.pc = m_regs.npc;
|
||||||
|
|
||||||
if (!DoAlignmentCheck<MemoryAccessType::Read, MemoryAccessSize::Word>(static_cast<VirtualMemoryAddress>(m_regs.npc)))
|
if (!DoAlignmentCheck<MemoryAccessType::Read, MemoryAccessSize::Word>(
|
||||||
|
static_cast<VirtualMemoryAddress>(m_regs.npc)) ||
|
||||||
|
!DoMemoryAccess<MemoryAccessType::Read, MemoryAccessSize::Word, true, true>(
|
||||||
|
static_cast<VirtualMemoryAddress>(m_regs.npc), m_next_instruction.bits))
|
||||||
{
|
{
|
||||||
// this will call FetchInstruction() again when the pipeline is flushed.
|
// this will call FetchInstruction() again when the pipeline is flushed.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
DoMemoryAccess<MemoryAccessType::Read, MemoryAccessSize::Word, true, true>(
|
|
||||||
static_cast<VirtualMemoryAddress>(m_regs.npc), m_next_instruction.bits);
|
|
||||||
m_regs.npc += sizeof(m_next_instruction.bits);
|
m_regs.npc += sizeof(m_next_instruction.bits);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,7 @@ bool Core::DoMemoryAccess(VirtualMemoryAddress address, u32& value)
|
||||||
case 0x03: // KUSEG 1536M-2048M
|
case 0x03: // KUSEG 1536M-2048M
|
||||||
{
|
{
|
||||||
// Above 512mb raises an exception.
|
// Above 512mb raises an exception.
|
||||||
Panic("Bad user access");
|
RaiseException(is_instruction_fetch ? Exception::IBE : Exception::DBE);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,7 +95,7 @@ bool Core::DoMemoryAccess(VirtualMemoryAddress address, u32& value)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Panic("KSEG2 access");
|
RaiseException(is_instruction_fetch ? Exception::IBE : Exception::DBE);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,10 +24,19 @@ bool HostInterface::InitializeSystem(const char* filename, const char* save_stat
|
||||||
const StaticString filename_str(filename);
|
const StaticString filename_str(filename);
|
||||||
if (filename_str.EndsWith(".psxexe", false) || filename_str.EndsWith(".exe", false))
|
if (filename_str.EndsWith(".psxexe", false) || filename_str.EndsWith(".exe", false))
|
||||||
{
|
{
|
||||||
Log_InfoPrintf("Sideloading EXE file %s", filename);
|
Log_InfoPrintf("Sideloading EXE file '%s'", filename);
|
||||||
if (!m_system->LoadEXE(filename))
|
if (!m_system->LoadEXE(filename))
|
||||||
{
|
{
|
||||||
Log_ErrorPrintf("Failed to load EXE file %s", filename);
|
Log_ErrorPrintf("Failed to load EXE file '%s'", filename);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log_InfoPrintf("Inserting CDROM from image file '%s'", filename);
|
||||||
|
if (!m_system->InsertMedia(filename))
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("Failed to insert media '%s'", filename);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -213,3 +213,18 @@ void System::SetPadDevice(u32 slot, std::shared_ptr<PadDevice> dev)
|
||||||
{
|
{
|
||||||
m_pad->SetDevice(slot, std::move(dev));
|
m_pad->SetDevice(slot, std::move(dev));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool System::HasMedia() const
|
||||||
|
{
|
||||||
|
return m_cdrom->HasMedia();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool System::InsertMedia(const char* path)
|
||||||
|
{
|
||||||
|
return m_cdrom->InsertMedia(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void System::RemoveMedia()
|
||||||
|
{
|
||||||
|
m_cdrom->RemoveMedia();
|
||||||
|
}
|
||||||
|
|
|
@ -45,6 +45,10 @@ public:
|
||||||
|
|
||||||
void SetPadDevice(u32 slot, std::shared_ptr<PadDevice> dev);
|
void SetPadDevice(u32 slot, std::shared_ptr<PadDevice> dev);
|
||||||
|
|
||||||
|
bool HasMedia() const;
|
||||||
|
bool InsertMedia(const char* path);
|
||||||
|
void RemoveMedia();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool DoState(StateWrapper& sw);
|
bool DoState(StateWrapper& sw);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue