mirror of https://github.com/OpenRCT2/OpenRCT2.git
add scenario overrides
This commit is contained in:
parent
188978936b
commit
fddf057d68
|
@ -13,6 +13,9 @@ extern "C" {
|
|||
constexpr rct_string_id ObjectOverrideBase = 0x6000;
|
||||
constexpr int ObjectOverrideMaxStringCount = 4;
|
||||
|
||||
constexpr rct_string_id ScenarioOverrideBase = 0x7000;
|
||||
constexpr int ScenarioOverrideMaxStringCount = 3;
|
||||
|
||||
LanguagePack *LanguagePack::FromFile(int id, const utf8 *path)
|
||||
{
|
||||
assert(path != NULL);
|
||||
|
@ -54,6 +57,7 @@ LanguagePack::LanguagePack(int id, const utf8 *text)
|
|||
_stringData = NULL;
|
||||
_currentGroup = NULL;
|
||||
_currentObjectOverride = NULL;
|
||||
_currentScenarioOverride = NULL;
|
||||
|
||||
auto reader = UTF8StringReader(text);
|
||||
while (reader.CanRead()) {
|
||||
|
@ -74,6 +78,14 @@ LanguagePack::LanguagePack(int id, const utf8 *text)
|
|||
}
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < _scenarioOverrides.size(); i++) {
|
||||
for (int j = 0; j < ScenarioOverrideMaxStringCount; j++) {
|
||||
const utf8 **strPtr = &(_scenarioOverrides[i].strings[j]);
|
||||
if (*strPtr != NULL) {
|
||||
*strPtr = (utf8*)(stringDataBaseAddress + (size_t)*strPtr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Destruct the string builder to free memory
|
||||
_stringDataSB = StringBuilder();
|
||||
|
@ -86,7 +98,17 @@ LanguagePack::~LanguagePack()
|
|||
}
|
||||
|
||||
const utf8 *LanguagePack::GetString(int stringId) const {
|
||||
if (stringId >= ObjectOverrideBase) {
|
||||
if (stringId >= ScenarioOverrideBase) {
|
||||
int offset = stringId - ScenarioOverrideBase;
|
||||
int ooIndex = offset / ScenarioOverrideMaxStringCount;
|
||||
int ooStringIndex = offset % ScenarioOverrideMaxStringCount;
|
||||
|
||||
if (_scenarioOverrides.size() > (size_t)ooIndex) {
|
||||
return _scenarioOverrides[ooIndex].strings[ooStringIndex];
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}else if (stringId >= ObjectOverrideBase) {
|
||||
int offset = stringId - ObjectOverrideBase;
|
||||
int ooIndex = offset / ObjectOverrideMaxStringCount;
|
||||
int ooStringIndex = offset % ObjectOverrideMaxStringCount;
|
||||
|
@ -124,6 +146,25 @@ rct_string_id LanguagePack::GetObjectOverrideStringId(const char *objectIdentifi
|
|||
return STR_NONE;
|
||||
}
|
||||
|
||||
rct_string_id LanguagePack::GetScenarioOverrideStringId(const utf8 *scenarioFilename, int index)
|
||||
{
|
||||
assert(scenarioFilename != NULL);
|
||||
assert(index < ScenarioOverrideMaxStringCount);
|
||||
|
||||
int ooIndex = 0;
|
||||
for (const ScenarioOverride &scenarioOverride : _scenarioOverrides) {
|
||||
if (_stricmp(scenarioOverride.filename, scenarioFilename) == 0) {
|
||||
if (scenarioOverride.strings[index] == NULL) {
|
||||
return STR_NONE;
|
||||
}
|
||||
return ScenarioOverrideBase + (ooIndex * ScenarioOverrideMaxStringCount) + index;
|
||||
}
|
||||
ooIndex++;
|
||||
}
|
||||
|
||||
return STR_NONE;
|
||||
}
|
||||
|
||||
LanguagePack::ObjectOverride *LanguagePack::GetObjectOverride(const char *objectIdentifier)
|
||||
{
|
||||
assert(objectIdentifier != NULL);
|
||||
|
@ -137,6 +178,19 @@ LanguagePack::ObjectOverride *LanguagePack::GetObjectOverride(const char *object
|
|||
return false;
|
||||
}
|
||||
|
||||
LanguagePack::ScenarioOverride *LanguagePack::GetScenarioOverride(const utf8 *scenarioIdentifier)
|
||||
{
|
||||
assert(scenarioIdentifier != NULL);
|
||||
|
||||
for (size_t i = 0; i < _scenarioOverrides.size(); i++) {
|
||||
ScenarioOverride *so = &_scenarioOverrides[i];
|
||||
if (_stricmp(so->name, scenarioIdentifier) == 0) {
|
||||
return so;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Parsing
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -209,7 +263,10 @@ void LanguagePack::ParseLine(IStringReader *reader)
|
|||
SkipToEndOfLine(reader);
|
||||
break;
|
||||
case '[':
|
||||
ParseGroup(reader);
|
||||
ParseGroupObject(reader);
|
||||
break;
|
||||
case '<':
|
||||
ParseGroupScenario(reader);
|
||||
break;
|
||||
case '\r':
|
||||
case '\n':
|
||||
|
@ -223,7 +280,7 @@ void LanguagePack::ParseLine(IStringReader *reader)
|
|||
}
|
||||
}
|
||||
|
||||
void LanguagePack::ParseGroup(IStringReader *reader)
|
||||
void LanguagePack::ParseGroupObject(IStringReader *reader)
|
||||
{
|
||||
auto sb = StringBuilder();
|
||||
int codepoint;
|
||||
|
@ -253,6 +310,7 @@ void LanguagePack::ParseGroup(IStringReader *reader)
|
|||
if (sb.GetLength() == 8) {
|
||||
_currentGroup = sb.GetString();
|
||||
_currentObjectOverride = GetObjectOverride(_currentGroup);
|
||||
_currentScenarioOverride = NULL;
|
||||
if (_currentObjectOverride == NULL) {
|
||||
_objectOverrides.push_back(ObjectOverride());
|
||||
_currentObjectOverride = &_objectOverrides[_objectOverrides.size() - 1];
|
||||
|
@ -263,6 +321,42 @@ void LanguagePack::ParseGroup(IStringReader *reader)
|
|||
}
|
||||
}
|
||||
|
||||
void LanguagePack::ParseGroupScenario(IStringReader *reader)
|
||||
{
|
||||
auto sb = StringBuilder();
|
||||
int codepoint;
|
||||
|
||||
// Should have already deduced that the next codepoint is a <
|
||||
reader->Skip();
|
||||
|
||||
// Read string up to > or line end
|
||||
bool closedCorrectly = false;
|
||||
while (reader->TryPeek(&codepoint)) {
|
||||
if (IsNewLine(codepoint)) break;
|
||||
|
||||
reader->Skip();
|
||||
if (codepoint == '>') {
|
||||
closedCorrectly = true;
|
||||
break;
|
||||
}
|
||||
sb.Append(codepoint);
|
||||
}
|
||||
|
||||
if (closedCorrectly) {
|
||||
SafeFree(_currentGroup);
|
||||
|
||||
_currentGroup = sb.GetString();
|
||||
_currentObjectOverride = NULL;
|
||||
_currentScenarioOverride = GetScenarioOverride(_currentGroup);
|
||||
if (_currentScenarioOverride == NULL) {
|
||||
_scenarioOverrides.push_back(ScenarioOverride());
|
||||
_currentScenarioOverride = &_scenarioOverrides[_scenarioOverrides.size() - 1];
|
||||
memset(_currentScenarioOverride, 0, sizeof(ObjectOverride));
|
||||
_currentScenarioOverride->filename = sb.GetString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LanguagePack::ParseString(IStringReader *reader)
|
||||
{
|
||||
auto sb = StringBuilder();
|
||||
|
@ -344,7 +438,11 @@ void LanguagePack::ParseString(IStringReader *reader)
|
|||
|
||||
_strings[stringId] = relativeOffset;
|
||||
} else {
|
||||
_currentObjectOverride->strings[stringId] = relativeOffset;
|
||||
if (_currentObjectOverride != NULL) {
|
||||
_currentObjectOverride->strings[stringId] = relativeOffset;
|
||||
} else {
|
||||
_currentScenarioOverride->strings[stringId] = relativeOffset;
|
||||
}
|
||||
}
|
||||
|
||||
_stringDataSB.Append(sb.GetBuffer());
|
||||
|
|
|
@ -30,6 +30,7 @@ public:
|
|||
}
|
||||
|
||||
rct_string_id GetObjectOverrideStringId(const char *objectIdentifier, int index);
|
||||
rct_string_id GetScenarioOverrideStringId(const utf8 *scenarioFilename, int index);
|
||||
|
||||
private:
|
||||
struct ObjectOverride {
|
||||
|
@ -37,13 +38,27 @@ private:
|
|||
const utf8 *strings[4];
|
||||
};
|
||||
|
||||
struct ScenarioOverride {
|
||||
const utf8 *filename;
|
||||
union {
|
||||
const utf8 *strings[3];
|
||||
struct {
|
||||
const utf8 *name;
|
||||
const utf8 *park;
|
||||
const utf8 *details;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
int _id;
|
||||
utf8 *_stringData;
|
||||
std::vector<const utf8*> _strings;
|
||||
std::vector<ObjectOverride> _objectOverrides;
|
||||
std::vector<ScenarioOverride> _scenarioOverrides;
|
||||
|
||||
LanguagePack(int id, const utf8 *text);
|
||||
ObjectOverride *GetObjectOverride(const char *objectIdentifier);
|
||||
ScenarioOverride *GetScenarioOverride(const utf8 *scenarioFilename);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Parsing
|
||||
|
@ -51,9 +66,11 @@ private:
|
|||
StringBuilder _stringDataSB;
|
||||
utf8 *_currentGroup;
|
||||
ObjectOverride *_currentObjectOverride;
|
||||
ScenarioOverride *_currentScenarioOverride;
|
||||
|
||||
void ParseLine(IStringReader *reader);
|
||||
void ParseGroup(IStringReader *reader);
|
||||
void ParseGroupObject(IStringReader *reader);
|
||||
void ParseGroupScenario(IStringReader *reader);
|
||||
void ParseString(IStringReader *reader);
|
||||
|
||||
bool ParseToken(IStringReader *reader, uint32 *token);
|
||||
|
|
|
@ -385,4 +385,15 @@ rct_string_id object_get_localised_text(uint8_t** pStringTable/*ebp*/, int type/
|
|||
}
|
||||
}
|
||||
|
||||
bool language_get_localised_scenario_strings(const utf8 *scenarioFilename, rct_string_id *outStringIds)
|
||||
{
|
||||
outStringIds[0] = _languageCurrent->GetScenarioOverrideStringId(scenarioFilename, 0);
|
||||
outStringIds[1] = _languageCurrent->GetScenarioOverrideStringId(scenarioFilename, 1);
|
||||
outStringIds[2] = _languageCurrent->GetScenarioOverrideStringId(scenarioFilename, 2);
|
||||
return
|
||||
outStringIds[0] != (rct_string_id)STR_NONE ||
|
||||
outStringIds[1] != (rct_string_id)STR_NONE ||
|
||||
outStringIds[2] != (rct_string_id)STR_NONE;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -82,4 +82,6 @@ int utf8_length(const utf8 *text);
|
|||
wchar_t *utf8_to_widechar(const utf8 *src);
|
||||
utf8 *widechar_to_utf8(const wchar_t *src);
|
||||
|
||||
bool language_get_localised_scenario_strings(const utf8 *scenarioFilename, rct_string_id *outStringIds);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -73,14 +73,29 @@ int scenario_load_basic(const char *path, rct_s6_header *header, rct_s6_info *in
|
|||
SDL_RWclose(rw);
|
||||
RCT2_GLOBAL(0x009AA00C, uint8) = 0;
|
||||
|
||||
// Checks for a scenario string object (possibly for localisation)
|
||||
if ((info->entry.flags & 0xFF) != 255) {
|
||||
if (object_get_scenario_text(&info->entry)) {
|
||||
rct_stex_entry* stex_entry = RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_TEXT_TEMP_CHUNK, rct_stex_entry*);
|
||||
format_string(info->name, stex_entry->scenario_name, NULL);
|
||||
format_string(info->details, stex_entry->details, NULL);
|
||||
RCT2_GLOBAL(0x009AA00C, uint8) = stex_entry->var_06;
|
||||
object_free_scenario_text();
|
||||
// Get filename
|
||||
utf8 filename[MAX_PATH];
|
||||
strcpy(filename, path_get_filename(path));
|
||||
path_remove_extension(filename);
|
||||
|
||||
rct_string_id localisedStringIds[3];
|
||||
if (language_get_localised_scenario_strings(filename, localisedStringIds)) {
|
||||
if (localisedStringIds[0] != (rct_string_id)STR_NONE) {
|
||||
strncpy(info->name, language_get_string(localisedStringIds[0]), 64);
|
||||
}
|
||||
if (localisedStringIds[2] != (rct_string_id)STR_NONE) {
|
||||
strncpy(info->details, language_get_string(localisedStringIds[2]), 256);
|
||||
}
|
||||
} else {
|
||||
// Checks for a scenario string object (possibly for localisation)
|
||||
if ((info->entry.flags & 0xFF) != 255) {
|
||||
if (object_get_scenario_text(&info->entry)) {
|
||||
rct_stex_entry* stex_entry = RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_TEXT_TEMP_CHUNK, rct_stex_entry*);
|
||||
format_string(info->name, stex_entry->scenario_name, NULL);
|
||||
format_string(info->details, stex_entry->details, NULL);
|
||||
RCT2_GLOBAL(0x009AA00C, uint8) = stex_entry->var_06;
|
||||
object_free_scenario_text();
|
||||
}
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
|
@ -282,23 +297,45 @@ void scenario_begin()
|
|||
strcpy((char*)RCT2_ADDRESS_SCENARIO_DETAILS, s6Info->details);
|
||||
strcpy((char*)RCT2_ADDRESS_SCENARIO_NAME, s6Info->name);
|
||||
|
||||
rct_stex_entry* stex = g_stexEntries[0];
|
||||
if ((int)stex != -1) {
|
||||
char *buffer = (char*)RCT2_ADDRESS_COMMON_STRING_FORMAT_BUFFER;
|
||||
{
|
||||
// Get filename
|
||||
utf8 filename[MAX_PATH];
|
||||
strcpy(filename, _scenarioFileName);
|
||||
path_remove_extension(filename);
|
||||
|
||||
// Set localised park name
|
||||
format_string(buffer, stex->park_name, 0);
|
||||
park_set_name(buffer);
|
||||
rct_string_id localisedStringIds[3];
|
||||
if (language_get_localised_scenario_strings(filename, localisedStringIds)) {
|
||||
if (localisedStringIds[0] != (rct_string_id)STR_NONE) {
|
||||
strncpy((char*)RCT2_ADDRESS_SCENARIO_NAME, language_get_string(localisedStringIds[0]), 31);
|
||||
((char*)RCT2_ADDRESS_SCENARIO_NAME)[31] = '\0';
|
||||
}
|
||||
if (localisedStringIds[1] != (rct_string_id)STR_NONE) {
|
||||
park_set_name(language_get_string(localisedStringIds[1]));
|
||||
}
|
||||
if (localisedStringIds[2] != (rct_string_id)STR_NONE) {
|
||||
strncpy((char*)RCT2_ADDRESS_SCENARIO_DETAILS, language_get_string(localisedStringIds[2]), 255);
|
||||
((char*)RCT2_ADDRESS_SCENARIO_DETAILS)[255] = '\0';
|
||||
}
|
||||
} else {
|
||||
rct_stex_entry* stex = g_stexEntries[0];
|
||||
if ((int)stex != -1) {
|
||||
char *buffer = (char*)RCT2_ADDRESS_COMMON_STRING_FORMAT_BUFFER;
|
||||
|
||||
// Set localised scenario name
|
||||
format_string(buffer, stex->scenario_name, 0);
|
||||
strncpy((char*)RCT2_ADDRESS_SCENARIO_NAME, buffer, 31);
|
||||
((char*)RCT2_ADDRESS_SCENARIO_NAME)[31] = '\0';
|
||||
// Set localised park name
|
||||
format_string(buffer, stex->park_name, 0);
|
||||
park_set_name(buffer);
|
||||
|
||||
// Set localised scenario details
|
||||
format_string(buffer, stex->details, 0);
|
||||
strncpy((char*)RCT2_ADDRESS_SCENARIO_DETAILS, buffer, 255);
|
||||
((char*)RCT2_ADDRESS_SCENARIO_DETAILS)[255] = '\0';
|
||||
// Set localised scenario name
|
||||
format_string(buffer, stex->scenario_name, 0);
|
||||
strncpy((char*)RCT2_ADDRESS_SCENARIO_NAME, buffer, 31);
|
||||
((char*)RCT2_ADDRESS_SCENARIO_NAME)[31] = '\0';
|
||||
|
||||
// Set localised scenario details
|
||||
format_string(buffer, stex->details, 0);
|
||||
strncpy((char*)RCT2_ADDRESS_SCENARIO_DETAILS, buffer, 255);
|
||||
((char*)RCT2_ADDRESS_SCENARIO_DETAILS)[255] = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set the last saved game path
|
||||
|
|
Loading…
Reference in New Issue