mirror of
https://github.com/isledecomp/isle-portable.git
synced 2026-01-12 02:41:14 +00:00
214 lines
5.7 KiB
C++
214 lines
5.7 KiB
C++
#include "extensions/siloader.h"
|
|
|
|
#include "mxdsaction.h"
|
|
#include "mxmisc.h"
|
|
#include "mxstreamer.h"
|
|
|
|
#include <SDL3/SDL.h>
|
|
#include <interleaf.h>
|
|
|
|
using namespace Extensions;
|
|
|
|
std::map<std::string, std::string> SiLoader::options;
|
|
std::vector<std::string> SiLoader::files;
|
|
std::vector<std::pair<SiLoader::StreamObject, SiLoader::StreamObject>> SiLoader::startWith;
|
|
std::vector<std::pair<SiLoader::StreamObject, SiLoader::StreamObject>> SiLoader::removeWith;
|
|
std::vector<std::pair<SiLoader::StreamObject, SiLoader::StreamObject>> SiLoader::replace;
|
|
bool SiLoader::enabled = false;
|
|
|
|
void SiLoader::Initialize()
|
|
{
|
|
char* files = SDL_strdup(options["si loader:files"].c_str());
|
|
char* saveptr;
|
|
|
|
for (char* file = SDL_strtok_r(files, ",\n", &saveptr); file; file = SDL_strtok_r(NULL, ",\n", &saveptr)) {
|
|
SiLoader::files.emplace_back(file);
|
|
}
|
|
|
|
SDL_free(files);
|
|
}
|
|
|
|
bool SiLoader::Load()
|
|
{
|
|
for (const auto& file : files) {
|
|
LoadFile(file.c_str());
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
std::optional<MxCore*> SiLoader::HandleFind(StreamObject p_object, LegoWorld* world)
|
|
{
|
|
for (const auto& key : replace) {
|
|
if (key.first == p_object) {
|
|
return world->Find(key.second.first, key.second.second);
|
|
}
|
|
}
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::optional<MxResult> SiLoader::HandleStart(MxDSAction& p_action)
|
|
{
|
|
StreamObject object{p_action.GetAtomId(), p_action.GetObjectId()};
|
|
|
|
for (const auto& key : startWith) {
|
|
if (key.first == object) {
|
|
MxDSAction action;
|
|
action.SetAtomId(key.second.first);
|
|
action.SetObjectId(key.second.second);
|
|
action.SetUnknown24(p_action.GetUnknown24());
|
|
action.SetNotificationObject(p_action.GetNotificationObject());
|
|
action.SetOrigin(p_action.GetOrigin());
|
|
Start(&action);
|
|
}
|
|
}
|
|
|
|
for (const auto& key : replace) {
|
|
if (key.first == object) {
|
|
MxDSAction action;
|
|
action.SetAtomId(key.second.first);
|
|
action.SetObjectId(key.second.second);
|
|
action.SetUnknown24(p_action.GetUnknown24());
|
|
action.SetNotificationObject(p_action.GetNotificationObject());
|
|
action.SetOrigin(p_action.GetOrigin());
|
|
|
|
MxResult result = Start(&action);
|
|
p_action.SetUnknown24(action.GetUnknown24());
|
|
return result;
|
|
}
|
|
}
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::optional<MxBool> SiLoader::HandleRemove(StreamObject p_object, LegoWorld* world)
|
|
{
|
|
for (const auto& key : removeWith) {
|
|
if (key.first == p_object) {
|
|
RemoveFromWorld(key.second.first, key.second.second, world->GetAtomId(), world->GetEntityId());
|
|
}
|
|
}
|
|
|
|
for (const auto& key : replace) {
|
|
if (key.first == p_object) {
|
|
return RemoveFromWorld(key.second.first, key.second.second, world->GetAtomId(), world->GetEntityId());
|
|
}
|
|
}
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::optional<MxBool> SiLoader::HandleDelete(MxDSAction& p_action)
|
|
{
|
|
StreamObject object{p_action.GetAtomId(), p_action.GetObjectId()};
|
|
|
|
for (const auto& key : removeWith) {
|
|
if (key.first == object) {
|
|
MxDSAction action;
|
|
action.SetAtomId(key.second.first);
|
|
action.SetObjectId(key.second.second);
|
|
action.SetUnknown24(p_action.GetUnknown24());
|
|
action.SetNotificationObject(p_action.GetNotificationObject());
|
|
action.SetOrigin(p_action.GetOrigin());
|
|
DeleteObject(action);
|
|
}
|
|
}
|
|
|
|
for (const auto& key : replace) {
|
|
if (key.first == object) {
|
|
MxDSAction action;
|
|
action.SetAtomId(key.second.first);
|
|
action.SetObjectId(key.second.second);
|
|
action.SetUnknown24(p_action.GetUnknown24());
|
|
action.SetNotificationObject(p_action.GetNotificationObject());
|
|
action.SetOrigin(p_action.GetOrigin());
|
|
|
|
DeleteObject(action);
|
|
p_action.SetUnknown24(action.GetUnknown24());
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
bool SiLoader::LoadFile(const char* p_file)
|
|
{
|
|
si::Interleaf si;
|
|
MxStreamController* controller;
|
|
|
|
MxString path = MxString(MxOmni::GetHD()) + p_file;
|
|
path.MapPathToFilesystem();
|
|
if (si.Read(path.GetData()) != si::Interleaf::ERROR_SUCCESS) {
|
|
path = MxString(MxOmni::GetCD()) + p_file;
|
|
path.MapPathToFilesystem();
|
|
if (si.Read(path.GetData()) != si::Interleaf::ERROR_SUCCESS) {
|
|
SDL_Log("Could not parse SI file %s", p_file);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!(controller = Streamer()->Open(p_file, MxStreamer::e_diskStream))) {
|
|
SDL_Log("Could not load SI file %s", p_file);
|
|
return false;
|
|
}
|
|
|
|
ParseDirectives(controller->GetAtom(), &si);
|
|
return true;
|
|
}
|
|
|
|
void SiLoader::ParseDirectives(const MxAtomId& p_atom, si::Core* p_core, MxAtomId p_parentReplacedAtom)
|
|
{
|
|
for (si::Core* child : p_core->GetChildren()) {
|
|
MxAtomId replacedAtom = p_parentReplacedAtom;
|
|
|
|
if (si::Object* object = dynamic_cast<si::Object*>(child)) {
|
|
if (object->type() != si::MxOb::Null) {
|
|
std::string extra(object->extra_.data(), object->extra_.size());
|
|
const char* directive;
|
|
char atom[256];
|
|
uint32_t id;
|
|
|
|
if ((directive = SDL_strstr(extra.c_str(), "StartWith:"))) {
|
|
if (SDL_sscanf(directive, "StartWith:%255[^;];%d", atom, &id) == 2) {
|
|
startWith.emplace_back(
|
|
StreamObject{MxAtomId{atom, e_lowerCase2}, id},
|
|
StreamObject{p_atom, object->id_}
|
|
);
|
|
}
|
|
}
|
|
|
|
if ((directive = SDL_strstr(extra.c_str(), "RemoveWith:"))) {
|
|
if (SDL_sscanf(directive, "RemoveWith:%255[^;];%d", atom, &id) == 2) {
|
|
removeWith.emplace_back(
|
|
StreamObject{MxAtomId{atom, e_lowerCase2}, id},
|
|
StreamObject{p_atom, object->id_}
|
|
);
|
|
}
|
|
}
|
|
|
|
if (p_parentReplacedAtom.GetInternal()) {
|
|
replace.emplace_back(
|
|
StreamObject{p_parentReplacedAtom, object->id_},
|
|
StreamObject{p_atom, object->id_}
|
|
);
|
|
}
|
|
else {
|
|
if ((directive = SDL_strstr(extra.c_str(), "Replace:"))) {
|
|
if (SDL_sscanf(directive, "Replace:%255[^;];%d", atom, &id) == 2) {
|
|
replace.emplace_back(
|
|
StreamObject{MxAtomId{atom, e_lowerCase2}, id},
|
|
StreamObject{p_atom, object->id_}
|
|
);
|
|
replacedAtom = replace.back().first.first;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ParseDirectives(p_atom, child, replacedAtom);
|
|
}
|
|
}
|