/*
* xSF - GSF Player
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
*
* Based on a modified viogsf v0.08
*
* Partially based on the vio*sf framework
*
* Utilizes a modified VBA-M, SVN revision 1102, for playback
* http://vba-m.com/
*/
#include <algorithm>
#include <filesystem>
#include <memory>
#include <string>
#include <vector>
#include <cstddef>
#include <cstdint>
#include <zlib.h>
#include "XSFCommon.h"
#include "XSFPlayer.h"
#include "vbam/gba/Globals.h"
#include "vbam/gba/Sound.h"
#include "vbam/common/SoundDriver.h"
class XSFPlayer_GSF : public XSFPlayer
{
public:
XSFPlayer_GSF(const std::filesystem::path &path);
~XSFPlayer_GSF() override { this->Terminate(); }
bool Load() override;
void GenerateSamples(std::vector<std::uint8_t> &buf, unsigned offset, unsigned samples) override;
void Terminate() override;
};
const char *XSFPlayer::WinampDescription = "GSF Decoder";
const char *XSFPlayer::WinampExts = "gsf;minigsf\0Game Boy Advance Sound Format files (*.gsf;*.minigsf)\0";
XSFPlayer *XSFPlayer::Create(const std::filesystem::path &path)
{
return new XSFPlayer_GSF(path);
}
static struct
{
std::vector<std::uint8_t> rom;
unsigned entry;
} loaderwork = { std::vector<std::uint8_t>(), 0 };
int mapgsf(std::uint8_t *d, int l, int &s)
{
if (static_cast<std::size_t>(l) > loaderwork.rom.size())
l = loaderwork.rom.size();
if (l)
std::copy_n(&loaderwork.rom[0], l, d);
s = l;
return l;
}
static struct
{
std::vector<std::uint8_t> buf;
std::uint32_t len, fil, cur;
} buffer = { std::vector<std::uint8_t>(), 0, 0, 0 };
class GSFSoundDriver : public SoundDriver
{
void freebuffer()
{
buffer.buf.clear();
buffer.len = buffer.fil = buffer.cur = 0;
}
public:
bool init(long sampleRate)
{
freebuffer();
std::int32_t len = (sampleRate / 10) << 2;
buffer.buf.resize(len);
buffer.len = len;
return true;
}
void pause()
{
}
void reset()
{
}
void resume()
{
}
void write(std::uint16_t *finalWave, int length)
{
if (static_cast<std::uint32_t>(length) > buffer.len - buffer.fil)
length = buffer.len - buffer.fil;
if (length > 0)
{
std::copy_n(reinterpret_cast<std::uint8_t *>(finalWave), length, &buffer.buf[buffer.fil]);
buffer.fil += length;
}
}
void setThrottle(unsigned short)
{
}
};
SoundDriver *systemSoundInit()
{
return new GSFSoundDriver();
}
static void MapGSFSection(const std::vector<std::uint8_t> §ion, int level)
{
auto &data = loaderwork.rom;
std::uint32_t entry = Get32BitsLE(§ion[0]), offset = Get32BitsLE(§ion[4]) & 0x1FFFFFF, size = Get32BitsLE(§ion[8]), finalSize = size + offset;
if (level == 1)
loaderwork.entry = entry;
finalSize = NextHighestPowerOf2(finalSize);
if (data.empty())
data.resize(finalSize + 10, 0);
else if (data.size() < size + offset)
data.resize(offset + finalSize + 10);
std::copy_n(§ion[12], size, &data[offset]);
}
static bool MapGSF(XSFFile *xSF, int level)
{
if (!xSF->IsValidType(0x22))
return false;
auto &programSection = xSF->GetProgramSection();
if (!programSection.empty())
MapGSFSection(programSection, level);
return true;
}
static bool RecursiveLoadGSF(XSFFile *xSF, int level)
{
if (level <= 10 && xSF->GetTagExists("_lib"))
{
auto libxSF = std::make_unique<XSFFile>(xSF->GetFilepath().parent_path() / xSF->GetTagValue("_lib"), 8, 12);
if (!RecursiveLoadGSF(libxSF.get(), level + 1))
return false;
}
if (!MapGSF(xSF, level))
return false;
unsigned n = 2;
bool found;
do
{
found = false;
std::string libTag = "_lib" + std::to_string(n++);
if (xSF->GetTagExists(libTag))
{
found = true;
auto libxSF = std::make_unique<XSFFile>(xSF->GetFilepath().parent_path() / xSF->GetTagValue(libTag), 8, 12);
if (!RecursiveLoadGSF(libxSF.get(), level + 1))
return false;
}
} while (found);
return true;
}
static bool LoadGSF(XSFFile *xSF)
{
loaderwork.rom.clear();
loaderwork.entry = 0;
return RecursiveLoadGSF(xSF, 1);
}
XSFPlayer_GSF::XSFPlayer_GSF(const std::filesystem::path &path) : XSFPlayer()
{
this->xSF.reset(new XSFFile(path, 8, 12));
}
bool XSFPlayer_GSF::Load()
{
if (!LoadGSF(this->xSF.get()))
return false;
cpuIsMultiBoot = (loaderwork.entry >> 24) == 2;
CPULoadRom();
soundSetSampleRate(this->sampleRate);
soundInit();
soundReset();
soundSetEnable(0x30F);
CPUInit();
CPUReset();
return XSFPlayer::Load();
}
void XSFPlayer_GSF::GenerateSamples(std::vector<std::uint8_t> &buf, unsigned offset, unsigned samples)
{
unsigned bytes = samples << 2;
while (bytes)
{
unsigned remainbytes = buffer.fil - buffer.cur;
while (!remainbytes)
{
buffer.cur = buffer.fil = 0;
CPULoop(250000);
remainbytes = buffer.fil - buffer.cur;
}
unsigned len = remainbytes;
if (len > bytes)
len = bytes;
std::copy_n(&buffer.buf[buffer.cur], len, &buf[offset]);
bytes -= len;
offset += len;
buffer.cur += len;
}
}
void XSFPlayer_GSF::Terminate()
{
soundShutdown();
loaderwork.rom.clear();
loaderwork.entry = 0;
}