Compare commits

...

4 commits

17 changed files with 220 additions and 64 deletions

View file

@ -799,7 +799,6 @@
<ClCompile Include="NES\HdPacks\HdPackLoader.cpp" />
<ClCompile Include="NES\HdPacks\HdVideoFilter.cpp" />
<ClCompile Include="NES\HdPacks\OggMixer.cpp" />
<ClCompile Include="NES\HdPacks\OggReader.cpp" />
<ClCompile Include="NES\Loaders\FdsLoader.cpp" />
<ClCompile Include="NES\Loaders\iNesLoader.cpp" />
<ClCompile Include="NES\Loaders\NsfLoader.cpp" />
@ -979,6 +978,7 @@
<ClCompile Include="SNES\SnesNtscFilter.cpp" />
<ClCompile Include="SNES\Coprocessors\OBC1\Obc1.cpp" />
<ClCompile Include="Shared\Audio\PcmReader.cpp" />
<ClCompile Include="Shared\Audio\OggReader.cpp" />
<ClCompile Include="SNES\SnesPpu.cpp" />
<ClCompile Include="Debugger\PpuTools.cpp" />
<ClCompile Include="Debugger\Profiler.cpp" />
@ -1240,4 +1240,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>

View file

@ -844,12 +844,6 @@
<ClInclude Include="NES\HdPacks\OggMixer.h">
<Filter>NES\HdPacks</Filter>
</ClInclude>
<ClCompile Include="NES\HdPacks\OggReader.cpp">
<Filter>NES\HdPacks</Filter>
</ClCompile>
<ClInclude Include="NES\HdPacks\OggReader.h">
<Filter>NES\HdPacks</Filter>
</ClInclude>
<ClInclude Include="NES\Input\ArkanoidController.h">
<Filter>NES\Input</Filter>
</ClInclude>
@ -979,6 +973,12 @@
<ClInclude Include="Shared\Audio\PcmReader.h">
<Filter>Shared\Audio</Filter>
</ClInclude>
<ClCompile Include="Shared\Audio\OggReader.cpp">
<Filter>Shared\Audio</Filter>
</ClCompile>
<ClInclude Include="Shared\Audio\OggReader.h">
<Filter>Shared\Audio</Filter>
</ClInclude>
<ClCompile Include="Shared\Audio\SoundMixer.cpp">
<Filter>Shared\Audio</Filter>
</ClCompile>
@ -3185,4 +3185,4 @@
<UniqueIdentifier>{4cc96bee-c354-4a9b-9947-85959ccc072f}</UniqueIdentifier>
</Filter>
</ItemGroup>
</Project>
</Project>

View file

@ -1,6 +1,6 @@
#include "pch.h"
#include <algorithm>
#include "NES/HdPacks/OggReader.h"
#include "Shared/Audio/OggReader.h"
#include "NES/HdPacks/OggMixer.h"
enum class OggPlaybackOptions

View file

@ -913,13 +913,18 @@ template<class T> void NesPpu<T>::ProcessScanlineImpl()
//copy vertical scrolling value from t
_videoRamAddr = (_videoRamAddr & ~0x7BE0) | (_tmpVideoRamAddr & 0x7BE0);
}
// Load the extra sprites before tile loading starts (which matters for MMC5)
// The CHR banks are switched back to the bg tileset in LoadTileInfo on cycle 321
// and the extra sprites will potentially read from the wrong banks
if(_cycle == 320) {
LoadExtraSprites();
}
}
} else if(_cycle >= 321 && _cycle <= 336) {
LoadTileInfo();
if(_cycle == 321) {
if(IsRenderingEnabled()) {
LoadExtraSprites();
_oamCopybuffer = _secondarySpriteRam[0];
}
} else if(_prevRenderingEnabled && (_cycle == 328 || _cycle == 336)) {

View file

@ -5,14 +5,80 @@
#include "Shared/Audio/SoundMixer.h"
#include "Utilities/Serializer.h"
#include "Utilities/FolderUtilities.h"
#include "Utilities/StringUtilities.h"
bool ends_with(std::string const & value, std::string const & ending)
{
if (ending.size() > value.size()) return false;
return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
}
std::optional<std::string> GetFileForExt(std::string& base, std::string ext) {
if (base.back() == '\x1') {
auto withoutEnd = base;
withoutEnd.pop_back();
auto reader = ArchiveReader::GetReader(withoutEnd);
for (const auto& f : reader->GetFileList()) {
if (ends_with(StringUtilities::ToLower(f), ext)) {
return base + f;
}
}
} else {
auto ret = base + ext;
if (VirtualFile(ret).IsValid()) {
return ret;
}
}
return std::nullopt;
}
void PcmOrOggReader::SetLoopFlag(bool loop) {
if (_usingOgg) { _oggReader.SetLoopFlag(loop); }
else { _pcmReader.SetLoopFlag(loop); }
}
void PcmOrOggReader::SetSampleRate(uint32_t sampleRate) {
if (_usingOgg) { _oggReader.SetSampleRate(sampleRate); }
else { _pcmReader.SetSampleRate(sampleRate); }
}
void PcmOrOggReader::ApplySamples(int16_t* buffer, size_t sampleCount, uint8_t volume) {
if (_usingOgg) { _oggReader.ApplySamples(buffer, sampleCount, volume); }
else { _pcmReader.ApplySamples(buffer, sampleCount, volume); }
}
bool PcmOrOggReader::IsPlaybackOver() {
return _usingOgg ? _oggReader.IsPlaybackOver() : _pcmReader.IsPlaybackOver();
}
uint32_t PcmOrOggReader::GetOffset() {
return _usingOgg ? _oggReader.GetOffset() * 4 : _pcmReader.GetOffset();
}
bool PcmOrOggReader::Init(string base, size_t track, bool loop, uint32_t startOffset) {
_usingOgg = false;
auto pcm = GetFileForExt(base, "-" + std::to_string(track) + ".pcm");
if (pcm.has_value()) {
if (_pcmReader.Init(*pcm, loop, startOffset)) {
return true;
}
return false;
}
auto ogg = GetFileForExt(base, "-" + std::to_string(track) + ".ogg");
if (ogg.has_value()) {
if (_oggReader.Init(*ogg, loop, 44100, startOffset / 4)) {
_usingOgg = true;
return true;
}
return false;
}
return false;
}
Msu1* Msu1::Init(Emulator* emu, VirtualFile& romFile, Spc* spc)
{
string romFolder = romFile.GetFolderPath();
string romName = FolderUtilities::GetFilename(romFile.GetFileName(), false);
if(ifstream(FolderUtilities::CombinePath(romFolder, romName + ".msu"))) {
if(romFile.IsArchive()) {
return new Msu1(emu, romFile, spc);
} else if(ifstream(FolderUtilities::CombinePath(romFolder, "msu1.rom"))) {
} else if(VirtualFile(FolderUtilities::CombinePath(romFolder, romName + ".msu")).IsValid()) {
return new Msu1(emu, romFile, spc);
} else if(VirtualFile(FolderUtilities::CombinePath(romFolder, "msu1.rom")).IsValid()) {
return new Msu1(emu, romFile, spc);
} else {
return nullptr;
@ -25,17 +91,28 @@ Msu1::Msu1(Emulator* emu, VirtualFile& romFile, Spc* spc)
_spc = spc;
_romFolder = romFile.GetFolderPath();
_romName = FolderUtilities::GetFilename(romFile.GetFileName(), false);
_dataFile.open(FolderUtilities::CombinePath(_romFolder, _romName) + ".msu", ios::binary);
if(_dataFile) {
if (romFile.IsArchive()) {
_trackPath = romFile.GetFilePath() + "\x1";
auto path = GetFileForExt(_trackPath, ".msu");
if (path.has_value()) {
auto dataVirtualFileArchive = VirtualFile(*path);
_dataFile = dataVirtualFileArchive.Stream();
}
} else if(
auto dataVirtualFile = VirtualFile(FolderUtilities::CombinePath(_romFolder, _romName) + ".msu");
dataVirtualFile.IsValid()
) {
_dataFile = dataVirtualFile.Stream();
_trackPath = FolderUtilities::CombinePath(_romFolder, _romName);
} else {
_dataFile.open(FolderUtilities::CombinePath(_romFolder, "msu1.rom"), ios::binary);
auto dataVirtualFileRom = VirtualFile(FolderUtilities::CombinePath(_romFolder, "msu1.rom"));
_dataFile = dataVirtualFileRom.Stream();
_trackPath = FolderUtilities::CombinePath(_romFolder, "track");
}
if(_dataFile) {
_dataFile.seekg(0, ios::end);
_dataSize = (uint32_t)_dataFile.tellg();
_dataFile->seekg(0, ios::end);
_dataSize = (uint32_t)_dataFile->tellg();
} else {
_dataSize = 0;
}
@ -57,7 +134,7 @@ void Msu1::Write(uint16_t addr, uint8_t value)
case 0x2003:
_tmpDataPointer = (_tmpDataPointer & 0x00FFFFFF) | (value << 24);
_dataPointer = _tmpDataPointer;
_dataFile.seekg(_dataPointer, ios::beg);
_dataFile->seekg(_dataPointer, ios::beg);
break;
case 0x2004: _trackSelect = (_trackSelect & 0xFF00) | value; break;
@ -71,7 +148,7 @@ void Msu1::Write(uint16_t addr, uint8_t value)
if(!_audioBusy) {
_repeat = (value & 0x02) != 0;
_paused = (value & 0x01) == 0;
_pcmReader.SetLoopFlag(_repeat);
_soundReader.SetLoopFlag(_repeat);
}
break;
}
@ -88,7 +165,7 @@ uint8_t Msu1::Read(uint16_t addr)
//data
if(!_dataBusy && _dataPointer < _dataSize) {
_dataPointer++;
return (uint8_t)_dataFile.get();
return (uint8_t)_dataFile->get();
}
return 0;
@ -106,24 +183,26 @@ uint8_t Msu1::Read(uint16_t addr)
void Msu1::MixAudio(int16_t* buffer, uint32_t sampleCount, uint32_t sampleRate)
{
if(!_paused) {
_pcmReader.SetSampleRate(sampleRate);
_pcmReader.ApplySamples(buffer, (size_t)sampleCount, _spc->IsMuted() ? 0 : _volume);
_soundReader.SetSampleRate(sampleRate);
_soundReader.ApplySamples(buffer, (size_t)sampleCount, _spc->IsMuted() ? 0 : _volume);
_paused |= _pcmReader.IsPlaybackOver();
_paused |= _soundReader.IsPlaybackOver();
}
}
void Msu1::LoadTrack(uint32_t startOffset)
{
_trackMissing = !_pcmReader.Init(_trackPath + "-" + std::to_string(_trackSelect) + ".pcm", _repeat, startOffset);
_trackMissing = !_soundReader.Init(_trackPath, _trackSelect, _repeat, startOffset);
}
void Msu1::Serialize(Serializer &s)
{
uint32_t offset = _pcmReader.GetOffset();
uint32_t offset = _soundReader.GetOffset();
SV(_trackSelect); SV(_tmpDataPointer); SV(_dataPointer); SV(_repeat); SV(_paused); SV(_volume); SV(_trackMissing); SV(_audioBusy); SV(_dataBusy); SV(offset);
if(!s.IsSaving()) {
_dataFile.seekg(_dataPointer, ios::beg);
if (_dataFile) {
_dataFile->seekg(_dataPointer, ios::beg);
}
LoadTrack(offset);
}
}

View file

@ -1,19 +1,38 @@
#pragma once
#include "pch.h"
#include "Shared/Interfaces/IAudioProvider.h"
#include "Shared/Audio/PcmReader.h"
#include "Shared/Audio/OggReader.h"
#include "Utilities/ISerializable.h"
#include "Utilities/VirtualFile.h"
#include "Utilities/StringUtilities.h"
#include "Utilities/ArchiveReader.h"
class Spc;
class Emulator;
class PcmOrOggReader final {
private:
PcmReader _pcmReader;
OggReader _oggReader;
bool _usingOgg = false;
public:
void SetLoopFlag(bool loop);
void SetSampleRate(uint32_t sampleRate);
void ApplySamples(int16_t* buffer, size_t sampleCount, uint8_t volume);
bool IsPlaybackOver();
uint32_t GetOffset();
bool Init(string base, size_t track, bool loop, uint32_t startOffset);
};
class Msu1 final : public ISerializable, public IAudioProvider
{
private:
Spc* _spc = nullptr;
Emulator* _emu = nullptr;
PcmReader _pcmReader;
PcmOrOggReader _soundReader;
uint8_t _volume = 100;
uint16_t _trackSelect = 0;
uint32_t _tmpDataPointer = 0;
@ -28,7 +47,7 @@ private:
bool _dataBusy = false; //Always false
bool _trackMissing = false;
ifstream _dataFile;
unique_ptr<istream> _dataFile;
uint32_t _dataSize;
void LoadTrack(uint32_t startOffset = 8);
@ -45,4 +64,4 @@ public:
void MixAudio(int16_t* buffer, uint32_t sampleCount, uint32_t sampleRate) override;
void Serialize(Serializer &s) override;
};
};

View file

@ -32,6 +32,7 @@
#include "Utilities/VirtualFile.h"
#include "Utilities/PlatformUtilities.h"
#include "Utilities/FolderUtilities.h"
#include "Utilities/StringUtilities.h"
#include "Shared/EventType.h"
#include "SNES/RegisterHandlerA.h"
#include "SNES/RegisterHandlerB.h"
@ -100,7 +101,7 @@ void SnesConsole::Reset()
_memoryManager->IncMasterClockStartup();
}
LoadRomResult SnesConsole::LoadRom(VirtualFile& romFile)
LoadRomResult SnesConsole::InnerLoadRom(VirtualFile& romFile)
{
SnesConfig config = _settings->GetSnesConfig();
LoadRomResult loadResult = LoadRomResult::UnknownType;
@ -144,6 +145,16 @@ LoadRomResult SnesConsole::LoadRom(VirtualFile& romFile)
return loadResult;
}
LoadRomResult SnesConsole::LoadRom(VirtualFile& romFile)
{
if (StringUtilities::ToLower(romFile.GetFileExtension()) == ".msu1") {
auto r = VirtualFile(romFile.GetFilePath(), "program.rom");
return this->InnerLoadRom(r);
} else {
return this->InnerLoadRom(romFile);
}
}
bool SnesConsole::LoadSpcFile(VirtualFile& romFile)
{
_spc->LoadSpcFile(_cart->GetSpcData());
@ -549,4 +560,4 @@ void SnesConsole::InitializeRam(void* data, uint32_t length)
state = settings->GetSnesConfig().RamPowerOnState;
}
settings->InitializeRam(state, data, length);
}
}

View file

@ -66,13 +66,14 @@ private:
bool _frameRunning = false;
void UpdateRegion();
LoadRomResult InnerLoadRom(VirtualFile& romFile);
bool LoadSpcFile(VirtualFile& romFile);
public:
SnesConsole(Emulator* emu);
~SnesConsole();
static vector<string> GetSupportedExtensions() { return { ".sfc", ".swc", ".fig", ".smc", ".bs", ".gb", ".gbc", ".spc" }; }
static vector<string> GetSupportedExtensions() { return { ".sfc", ".swc", ".fig", ".smc", ".bs", ".gb", ".gbc", ".spc", ".msu1" }; }
static vector<string> GetSupportedSignatures() { return { "SNES-SPC700 Sound File Data" }; }
void Initialize();

View file

@ -1,6 +1,7 @@
#include "pch.h"
#include "NES/HdPacks/OggReader.h"
#include "Shared/Audio/OggReader.h"
#include "Utilities/Audio/stb_vorbis.h"
#include "Utilities/StringUtilities.h"
OggReader::OggReader()
{
@ -21,12 +22,29 @@ OggReader::~OggReader()
bool OggReader::Init(string filename, bool loop, uint32_t sampleRate, uint32_t startOffset, uint32_t loopPosition)
{
_loaded = false;
int error;
VirtualFile file = filename;
_fileData = vector<uint8_t>(100000);
if(file.ReadFile(_fileData)) {
_vorbis = stb_vorbis_open_memory(_fileData.data(), (int)_fileData.size(), &error, nullptr);
if(_vorbis) {
auto comments = stb_vorbis_get_comment(_vorbis);
for (int i = 0; i < comments.comment_list_length; i++) {
auto comment = comments.comment_list[i];
vector<string> commentParts = StringUtilities::Split(comment, '=');
if (commentParts.size() != 2) { continue; }
if (commentParts[0] != "LOOPSTART") { continue; }
uint32_t loopPositionFromFile;
try {
loopPositionFromFile = std::stoi(commentParts[1]);
} catch (std::invalid_argument const& _e) {
throw std::runtime_error("Mis-formatted loop start in .ogg");
} catch (std::out_of_range const& _e) {
throw std::runtime_error("Out-of-range integer loop start in .ogg");
}
loopPosition = loopPositionFromFile;
}
_loop = loop;
if(loopPosition > 0) {
unsigned int sampleCount = stb_vorbis_stream_length_in_samples(_vorbis);
@ -38,6 +56,7 @@ bool OggReader::Init(string filename, bool loop, uint32_t sampleRate, uint32_t s
if(startOffset > 0) {
stb_vorbis_seek(_vorbis, startOffset);
}
_loaded = true;
return true;
}
}
@ -63,6 +82,9 @@ void OggReader::SetLoopFlag(bool loop)
void OggReader::ApplySamples(int16_t* buffer, size_t sampleCount, uint8_t volume)
{
if (!_loaded) {
return;
}
int32_t samplesNeeded = (int32_t)sampleCount - _resampler.GetPendingCount();
uint32_t samplesRead = 0;
if(samplesNeeded > 0) {
@ -88,5 +110,8 @@ void OggReader::ApplySamples(int16_t* buffer, size_t sampleCount, uint8_t volume
uint32_t OggReader::GetOffset()
{
return stb_vorbis_get_file_offset(_vorbis);
}
if (!_loaded) {
return 0;
}
return stb_vorbis_get_sample_offset(_vorbis);
}

View file

@ -14,6 +14,7 @@ private:
HermiteResampler _resampler;
bool _loaded = false;
bool _loop = false;
bool _done = false;

View file

@ -17,23 +17,21 @@ PcmReader::~PcmReader()
bool PcmReader::Init(string filename, bool loop, uint32_t startOffset)
{
if(_file) {
_file.close();
}
auto v = VirtualFile(filename);
_file = v.Stream();
_file.open(filename, ios::binary);
if(_file) {
_file.seekg(0, ios::end);
_fileSize = (uint32_t)_file.tellg();
_file->seekg(0, ios::end);
_fileSize = (uint32_t)_file->tellg();
if(_fileSize < 12) {
return false;
}
_file.seekg(4, ios::beg);
uint32_t loopOffset = (uint8_t)_file.get();
loopOffset |= ((uint8_t)_file.get()) << 8;
loopOffset |= ((uint8_t)_file.get()) << 16;
loopOffset |= ((uint8_t)_file.get()) << 24;
_file->seekg(4, ios::beg);
uint32_t loopOffset = (uint8_t)_file->get();
loopOffset |= ((uint8_t)_file->get()) << 8;
loopOffset |= ((uint8_t)_file->get()) << 16;
loopOffset |= ((uint8_t)_file->get()) << 24;
_loopOffset = (uint32_t)loopOffset;
@ -42,7 +40,7 @@ bool PcmReader::Init(string filename, bool loop, uint32_t startOffset)
_done = false;
_loop = loop;
_fileOffset = startOffset;
_file.seekg(_fileOffset, ios::beg);
_file->seekg(_fileOffset, ios::beg);
_leftoverSampleCount = 0;
_pcmBuffer.clear();
@ -77,10 +75,10 @@ void PcmReader::SetLoopFlag(bool loop)
void PcmReader::ReadSample(int16_t &left, int16_t &right)
{
uint8_t val[4];
_file.get(((char*)val)[0]);
_file.get(((char*)val)[1]);
_file.get(((char*)val)[2]);
_file.get(((char*)val)[3]);
_file->get(((char*)val)[0]);
_file->get(((char*)val)[1]);
_file->get(((char*)val)[2]);
_file->get(((char*)val)[3]);
left = val[0] | (val[1] << 8);
right = val[2] | (val[3] << 8);
@ -108,7 +106,7 @@ void PcmReader::LoadSamples(uint32_t samplesToLoad)
if(_loop) {
i = _loopOffset * 4 + 8;
_fileOffset = i;
_file.seekg(_fileOffset, ios::beg);
_file->seekg(_fileOffset, ios::beg);
} else {
_done = true;
}
@ -140,4 +138,4 @@ void PcmReader::ApplySamples(int16_t *buffer, size_t sampleCount, uint8_t volume
uint32_t PcmReader::GetOffset()
{
return _fileOffset;
}
}

View file

@ -11,7 +11,7 @@ private:
int16_t* _outputBuffer = nullptr;
ifstream _file;
unique_ptr<istream> _file;
uint32_t _fileOffset = 0;
uint32_t _fileSize = 0;
uint32_t _loopOffset = 0;

View file

@ -582,7 +582,7 @@
<Control ID="tpgFiles">Folders/Files</Control>
<Control ID="grpFileAssociations">File Associations</Control>
<Control ID="chkSnesRomFiles">SNES roms: .sfc, .smc, .swc, .fig, .bs</Control>
<Control ID="chkSnesRomFiles">SNES roms: .sfc, .smc, .swc, .fig, .bs, .msu1</Control>
<Control ID="chkSnesMusicFiles">SNES music: .spc</Control>
<Control ID="chkNesRomFiles">NES roms: .nes, .unif, .fds, .studybox</Control>
<Control ID="chkNesMusicFiles">NES music: .nsf, .nsfe</Control>

View file

@ -49,8 +49,8 @@ namespace Mesen.Utilities
List<FilePickerFileType> filter = new List<FilePickerFileType>();
foreach(string ext in extensions) {
if(ext == FileDialogHelper.RomExt) {
filter.Add(new FilePickerFileType("All ROM files") { Patterns = new List<string>() { "*.sfc", "*.fig", "*.smc", "*.bs", "*.spc", "*.nes", "*.fds", "*.unif", "*.unf", "*.studybox", "*.nsf", "*.nsfe", "*.gb", "*.gbc", "*.gbs", "*.pce", "*.sgx", "*.cue", "*.hes", "*.sms", "*.gg", "*.sg", "*.gba", "*.zip", "*.7z" } });
filter.Add(new FilePickerFileType("SNES ROM files") { Patterns = new List<string>() { "*.sfc", "*.fig", "*.smc", "*.bs", "*.spc" } });
filter.Add(new FilePickerFileType("All ROM files") { Patterns = new List<string>() { "*.sfc", "*.fig", "*.smc", "*.bs", "*.spc", "*.nes", "*.fds", "*.unif", "*.unf", "*.studybox", "*.nsf", "*.nsfe", "*.gb", "*.gbc", "*.gbs", "*.pce", "*.sgx", "*.cue", "*.hes", "*.sms", "*.gg", "*.sg", "*.gba", "*.zip", "*.7z", "*.msu1" } });
filter.Add(new FilePickerFileType("SNES ROM files") { Patterns = new List<string>() { "*.sfc", "*.fig", "*.smc", "*.bs", "*.spc", "*.msu1" } });
filter.Add(new FilePickerFileType("NES ROM files") { Patterns = new List<string>() { "*.nes", "*.fds", "*.unif", "*.unf", "*.studybox", "*.nsf", "*.nsfe" } });
filter.Add(new FilePickerFileType("GB ROM files") { Patterns = new List<string>() { "*.gb", "*.gbc", "*.gbs" } });
filter.Add(new FilePickerFileType("GBA ROM files") { Patterns = new List<string>() { "*.gba" } });
@ -138,4 +138,4 @@ namespace Mesen.Utilities
return null;
}
}
}
}

View file

@ -11,7 +11,7 @@ namespace Mesen.Utilities
public static class FolderHelper
{
private static HashSet<string> _romExtensions = new HashSet<string>() {
".sfc", ".smc", ".fig", ".swc", ".bs",
".sfc", ".smc", ".fig", ".swc", ".bs", ".msu1",
".gb", ".gbc",
".nes", ".unif", ".unf", ".fds", ".studybox",
".pce", ".sgx", ".cue",
@ -55,4 +55,4 @@ namespace Mesen.Utilities
return true;
}
}
}
}

View file

@ -13,7 +13,7 @@
const std::initializer_list<string> VirtualFile::RomExtensions = {
".nes", ".fds", ".unif", ".unf", ".nsf", ".nsfe", ".studybox",
".sfc", ".swc", ".fig", ".smc", ".bs", ".spc",
".sfc", ".swc", ".fig", ".smc", ".bs", ".spc", ".msu1",
".gb", ".gbc", ".gbs",
".pce", ".sgx", ".cue", ".hes",
".sms", ".gg", ".sg",
@ -265,6 +265,21 @@ bool VirtualFile::ReadFile(uint8_t* out, uint32_t expectedSize)
return false;
}
std::unique_ptr<std::istream> VirtualFile::Stream()
{
if(!_innerFile.empty()) {
auto out = std::make_unique<std::stringstream>();
ReadFile(*out);
return out;
} else {
auto ret = std::make_unique<std::ifstream>(_path, std::ios::in | std::ios::binary);
if(ret) {
return ret;
}
}
return std::make_unique<std::istream>(nullptr);
}
uint8_t VirtualFile::ReadByte(uint32_t offset)
{
InitChunks();
@ -310,4 +325,4 @@ bool VirtualFile::ApplyPatch(VirtualFile& patch)
}
}
return result;
}
}

View file

@ -48,6 +48,8 @@ public:
bool ReadFile(std::stringstream &out);
bool ReadFile(uint8_t* out, uint32_t expectedSize);
std::unique_ptr<std::istream> Stream();
uint8_t ReadByte(uint32_t offset);
bool ApplyPatch(VirtualFile &patch);
@ -67,4 +69,4 @@ public:
return true;
}
};
};