mirror of
https://github.com/izzy2lost/Ghostship.git
synced 2026-06-19 01:17:03 -07:00
277 lines
8.9 KiB
C++
277 lines
8.9 KiB
C++
#include "Engine.h"
|
|
#include "ui/ImguiUI.h"
|
|
#include "port/importer/AnimationFactory.h"
|
|
#include "port/importer/AudioBankFactory.h"
|
|
#include "port/importer/AudioSampleFactory.h"
|
|
#include "port/importer/AudioSequenceFactory.h"
|
|
#include "banks_table.h"
|
|
#include "sequences_table.h"
|
|
#include "audio/GameAudio.h"
|
|
#include "ZAPDUtils/Utils/StringHelper.h"
|
|
#include "texts_table.h"
|
|
#include "port/importer/DialogFactory.h"
|
|
#include "port/importer/DictionaryFactory.h"
|
|
#include <Fast3D/gfx_pc.h>
|
|
#include <Fast3D/gfx_rendering_api.h>
|
|
|
|
#include <utility>
|
|
|
|
extern "C" {
|
|
#include "audio/external.h"
|
|
#include "audio/internal.h"
|
|
#include "game/ingame_menu.h"
|
|
}
|
|
|
|
GameEngine* GameEngine::Instance;
|
|
|
|
GameEngine::GameEngine(){
|
|
std::vector<std::string> OTRFiles;
|
|
std::string cubePath = LUS::Context::GetPathRelativeToAppDirectory("smcube.otr");
|
|
if (std::filesystem::exists(cubePath)) {
|
|
OTRFiles.push_back(cubePath);
|
|
}
|
|
std::string sm64OtrPath = LUS::Context::GetPathRelativeToAppBundle("sm64.otr");
|
|
if (std::filesystem::exists(sm64OtrPath)) {
|
|
OTRFiles.push_back(sm64OtrPath);
|
|
}
|
|
std::string patchesPath = LUS::Context::GetPathRelativeToAppDirectory("mods");
|
|
if (patchesPath.length() > 0 && std::filesystem::exists(patchesPath)) {
|
|
if (std::filesystem::is_directory(patchesPath)) {
|
|
for (const auto& p : std::filesystem::recursive_directory_iterator(patchesPath)) {
|
|
if (StringHelper::IEquals(p.path().extension().string(), ".otr")) {
|
|
OTRFiles.push_back(p.path().generic_string());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
this->context = LUS::Context::CreateInstance("Ghostship", "sm64", "ghostship.cfg.json", OTRFiles, { 0xFF2B5A63 }, 3);
|
|
this->context->GetWindow()->SetTargetFps(30);
|
|
this->context->GetWindow()->SetMaximumFrameLatency(1);
|
|
this->context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory(LUS::ResourceType::SAnim, "Animation", std::make_shared<CubeOS::AnimationFactory>());
|
|
this->context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory(LUS::ResourceType::Bank, "Bank", std::make_shared<CubeOS::AudioBankFactory>());
|
|
this->context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory(LUS::ResourceType::Sample, "Sample", std::make_shared<CubeOS::AudioSampleFactory>());
|
|
this->context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory(LUS::ResourceType::Sequence, "Sequence", std::make_shared<CubeOS::AudioSequenceFactory>());
|
|
this->context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory(LUS::ResourceType::SDialog, "Dialog", std::make_shared<CubeOS::DialogFactory>());
|
|
this->context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory(LUS::ResourceType::Dictionary, "Dictionary", std::make_shared<CubeOS::DictionaryFactory>());
|
|
GameEngine::AudioInit();
|
|
}
|
|
|
|
void GameEngine::Create(){
|
|
GameEngine::Instance = new GameEngine();
|
|
GameUI::SetupGuiElements();
|
|
}
|
|
|
|
void GameEngine::Destroy(){
|
|
GameEngine::AudioExit();
|
|
}
|
|
|
|
bool ShouldClearTextureCacheAtEndOfFrame = false;
|
|
|
|
void GameEngine::StartFrame() const{
|
|
using LUS::KbScancode;
|
|
int32_t dwScancode = this->context->GetWindow()->GetLastScancode();
|
|
this->context->GetWindow()->SetLastScancode(-1);
|
|
|
|
switch (dwScancode) {
|
|
case KbScancode::LUS_KB_TAB: {
|
|
// Toggle HD Assets
|
|
CVarSetInteger("gAltAssets", !CVarGetInteger("gAltAssets", 0));
|
|
ShouldClearTextureCacheAtEndOfFrame = true;
|
|
break;
|
|
}
|
|
}
|
|
this->context->GetWindow()->StartFrame();
|
|
}
|
|
|
|
void GameEngine::RunCommands(Gfx* Commands) {
|
|
gfx_run(Commands, {});
|
|
gfx_end_frame();
|
|
|
|
if (ShouldClearTextureCacheAtEndOfFrame) {
|
|
gfx_texture_cache_clear();
|
|
ShouldClearTextureCacheAtEndOfFrame = false;
|
|
}
|
|
}
|
|
|
|
void GameEngine::ProcessFrame(void (*run_one_game_iter)()) const {
|
|
this->context->GetWindow()->MainLoop(run_one_game_iter);
|
|
}
|
|
|
|
// Audio
|
|
|
|
void GameEngine::HandleAudioThread(){
|
|
while (audio.running) {
|
|
{
|
|
std::unique_lock<std::mutex> Lock(audio.mutex);
|
|
while (!audio.processing && audio.running) {
|
|
audio.cv_to_thread.wait(Lock);
|
|
}
|
|
|
|
if (!audio.running) {
|
|
break;
|
|
}
|
|
}
|
|
std::unique_lock<std::mutex> Lock(audio.mutex);
|
|
|
|
int samples_left = AudioPlayerBuffered();
|
|
u32 num_audio_samples = samples_left < AudioPlayerGetDesiredBuffered() ? SAMPLES_HIGH : SAMPLES_LOW;
|
|
|
|
s16 audio_buffer[SAMPLES_PER_FRAME];
|
|
for (int i = 0; i < AUDIO_FRAMES_PER_UPDATE; i++) {
|
|
create_next_audio_buffer(audio_buffer + i * (num_audio_samples * 2), num_audio_samples);
|
|
}
|
|
|
|
AudioPlayerPlayFrame((u8 *) audio_buffer, 2 * num_audio_samples * 4);
|
|
|
|
audio.processing = false;
|
|
audio.cv_from_thread.notify_one();
|
|
}
|
|
}
|
|
|
|
void GameEngine::StartAudioFrame(){
|
|
{
|
|
std::unique_lock<std::mutex> Lock(audio.mutex);
|
|
audio.processing = true;
|
|
}
|
|
|
|
audio.cv_to_thread.notify_one();
|
|
}
|
|
|
|
void GameEngine::EndAudioFrame(){
|
|
{
|
|
std::unique_lock<std::mutex> Lock(audio.mutex);
|
|
while (audio.processing) {
|
|
audio.cv_from_thread.wait(Lock);
|
|
}
|
|
}
|
|
}
|
|
|
|
void GameEngine::AudioInit() {
|
|
LUS::Context::GetInstance()->GetResourceManager()->LoadDirectory("sound");
|
|
|
|
if (!audio.running) {
|
|
audio.running = true;
|
|
audio.thread = std::thread(GameEngine::HandleAudioThread);
|
|
}
|
|
}
|
|
|
|
void GameEngine::AudioExit() {
|
|
{
|
|
std::unique_lock<std::mutex> Lock(audio.mutex);
|
|
audio.running = false;
|
|
}
|
|
audio.cv_to_thread.notify_all();
|
|
|
|
// Wait until the audio thread quit
|
|
audio.thread.join();
|
|
}
|
|
|
|
void GameEngine::LoadDictionary() {
|
|
this->dictionary = static_cast<std::unordered_map<std::string, std::vector<uint8_t>> *>(ResourceGetDataByName("__OTR__texts/strings/global"));
|
|
int bp = 0;
|
|
}
|
|
|
|
extern "C" uint32_t GameEngine_GetSampleRate() {
|
|
auto player = LUS::Context::GetInstance()->GetAudio()->GetAudioPlayer();
|
|
if (player == nullptr) {
|
|
return 0;
|
|
}
|
|
|
|
if (!player->IsInitialized()) {
|
|
return 0;
|
|
}
|
|
|
|
return player->GetSampleRate();
|
|
}
|
|
|
|
extern "C" uint32_t GameEngine_GetSamplesPerFrame(){
|
|
return SAMPLES_PER_FRAME;
|
|
}
|
|
|
|
// End
|
|
|
|
extern "C" float GameEngine_GetAspectRatio() {
|
|
return gfx_current_dimensions.aspect_ratio;
|
|
}
|
|
|
|
extern "C" CtlEntry* GameEngine_LoadBank(uint8_t bankId) {
|
|
auto engine = GameEngine::Instance;
|
|
if(bankId > (sizeof(gBankTable) / sizeof(gBankTable[0])) - 1){
|
|
return nullptr;
|
|
}
|
|
if(engine->banks.contains(bankId)){
|
|
return engine->banks[bankId];
|
|
}
|
|
auto ctl = static_cast<CtlEntry *>(ResourceGetDataByName(gBankTable[bankId]));
|
|
engine->banks[bankId] = ctl;
|
|
return ctl;
|
|
}
|
|
|
|
extern "C" uint8_t GameEngine_IsBankLoaded(uint8_t bankId) {
|
|
auto engine = GameEngine::Instance;
|
|
GameEngine_LoadBank(bankId);
|
|
return engine->banks.contains(bankId);
|
|
}
|
|
|
|
extern "C" void GameEngine_UnloadBank(uint8_t bankId) {
|
|
auto engine = GameEngine::Instance;
|
|
if(engine->banks.contains(bankId)){
|
|
engine->banks.erase(bankId);
|
|
}
|
|
}
|
|
|
|
extern "C" AudioSequenceData* GameEngine_LoadSequence(uint8_t seqId) {
|
|
auto engine = GameEngine::Instance;
|
|
if(seqId > (sizeof(gSequenceTable) / sizeof(gSequenceTable[0])) - 1){
|
|
return nullptr;
|
|
}
|
|
if(engine->sequences.contains(seqId)){
|
|
return engine->sequences[seqId];
|
|
}
|
|
auto sequences = static_cast<AudioSequenceData *>(ResourceGetDataByName(gSequenceTable[seqId]));
|
|
engine->sequences[seqId] = sequences;
|
|
return sequences;
|
|
}
|
|
|
|
extern "C" uint8_t GameEngine_IsSequenceLoaded(uint8_t seqId) {
|
|
auto engine = GameEngine::Instance;
|
|
GameEngine_LoadSequence(seqId);
|
|
return engine->sequences.contains(seqId);
|
|
}
|
|
|
|
extern "C" void GameEngine_UnloadSequence(uint8_t seqId) {
|
|
auto engine = GameEngine::Instance;
|
|
if(engine->sequences.contains(seqId)){
|
|
engine->sequences.erase(seqId);
|
|
}
|
|
}
|
|
|
|
extern "C" uint32_t GameEngine_GetGameVersion() {
|
|
return LUS::Context::GetInstance()->GetResourceManager()->GetArchive()->GetGameVersions()[0];
|
|
}
|
|
|
|
extern "C" uint8_t* GameEngine_LoadActName(uint32_t actId){
|
|
return (uint8_t*) ResourceGetDataByName(StringHelper::Sprintf(gActRoot, actId).c_str());
|
|
}
|
|
|
|
extern "C" uint8_t* GameEngine_LoadLevelName(uint32_t courseId){
|
|
return (uint8_t*) ResourceGetDataByName(StringHelper::Sprintf(gCourseRoot, courseId).c_str());
|
|
}
|
|
|
|
extern "C" DialogEntry* GameEngine_LoadDialog(uint32_t dialogId){
|
|
auto dialog = (DialogEntry*) ResourceGetDataByName(StringHelper::Sprintf(gDialogRoot, dialogId).c_str());
|
|
return dialog;
|
|
}
|
|
|
|
extern "C" uint8_t* GameEngine_LoadTranslation(const char* key) {
|
|
auto engine = GameEngine::Instance;
|
|
auto dictionary = engine->dictionary;
|
|
|
|
if (dictionary == nullptr) {
|
|
engine->LoadDictionary();
|
|
}
|
|
|
|
assert(engine->dictionary != nullptr);
|
|
assert(engine->dictionary->contains(key));
|
|
return engine->dictionary->at(key).data();
|
|
} |