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 rct_string_id ObjectOverrideBase = 0x6000;
|
||||||
constexpr int ObjectOverrideMaxStringCount = 4;
|
constexpr int ObjectOverrideMaxStringCount = 4;
|
||||||
|
|
||||||
|
constexpr rct_string_id ScenarioOverrideBase = 0x7000;
|
||||||
|
constexpr int ScenarioOverrideMaxStringCount = 3;
|
||||||
|
|
||||||
LanguagePack *LanguagePack::FromFile(int id, const utf8 *path)
|
LanguagePack *LanguagePack::FromFile(int id, const utf8 *path)
|
||||||
{
|
{
|
||||||
assert(path != NULL);
|
assert(path != NULL);
|
||||||
|
@ -54,6 +57,7 @@ LanguagePack::LanguagePack(int id, const utf8 *text)
|
||||||
_stringData = NULL;
|
_stringData = NULL;
|
||||||
_currentGroup = NULL;
|
_currentGroup = NULL;
|
||||||
_currentObjectOverride = NULL;
|
_currentObjectOverride = NULL;
|
||||||
|
_currentScenarioOverride = NULL;
|
||||||
|
|
||||||
auto reader = UTF8StringReader(text);
|
auto reader = UTF8StringReader(text);
|
||||||
while (reader.CanRead()) {
|
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
|
// Destruct the string builder to free memory
|
||||||
_stringDataSB = StringBuilder();
|
_stringDataSB = StringBuilder();
|
||||||
|
@ -86,7 +98,17 @@ LanguagePack::~LanguagePack()
|
||||||
}
|
}
|
||||||
|
|
||||||
const utf8 *LanguagePack::GetString(int stringId) const {
|
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 offset = stringId - ObjectOverrideBase;
|
||||||
int ooIndex = offset / ObjectOverrideMaxStringCount;
|
int ooIndex = offset / ObjectOverrideMaxStringCount;
|
||||||
int ooStringIndex = offset % ObjectOverrideMaxStringCount;
|
int ooStringIndex = offset % ObjectOverrideMaxStringCount;
|
||||||
|
@ -124,6 +146,25 @@ rct_string_id LanguagePack::GetObjectOverrideStringId(const char *objectIdentifi
|
||||||
return STR_NONE;
|
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)
|
LanguagePack::ObjectOverride *LanguagePack::GetObjectOverride(const char *objectIdentifier)
|
||||||
{
|
{
|
||||||
assert(objectIdentifier != NULL);
|
assert(objectIdentifier != NULL);
|
||||||
|
@ -137,6 +178,19 @@ LanguagePack::ObjectOverride *LanguagePack::GetObjectOverride(const char *object
|
||||||
return false;
|
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
|
// Parsing
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -209,7 +263,10 @@ void LanguagePack::ParseLine(IStringReader *reader)
|
||||||
SkipToEndOfLine(reader);
|
SkipToEndOfLine(reader);
|
||||||
break;
|
break;
|
||||||
case '[':
|
case '[':
|
||||||
ParseGroup(reader);
|
ParseGroupObject(reader);
|
||||||
|
break;
|
||||||
|
case '<':
|
||||||
|
ParseGroupScenario(reader);
|
||||||
break;
|
break;
|
||||||
case '\r':
|
case '\r':
|
||||||
case '\n':
|
case '\n':
|
||||||
|
@ -223,7 +280,7 @@ void LanguagePack::ParseLine(IStringReader *reader)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LanguagePack::ParseGroup(IStringReader *reader)
|
void LanguagePack::ParseGroupObject(IStringReader *reader)
|
||||||
{
|
{
|
||||||
auto sb = StringBuilder();
|
auto sb = StringBuilder();
|
||||||
int codepoint;
|
int codepoint;
|
||||||
|
@ -253,6 +310,7 @@ void LanguagePack::ParseGroup(IStringReader *reader)
|
||||||
if (sb.GetLength() == 8) {
|
if (sb.GetLength() == 8) {
|
||||||
_currentGroup = sb.GetString();
|
_currentGroup = sb.GetString();
|
||||||
_currentObjectOverride = GetObjectOverride(_currentGroup);
|
_currentObjectOverride = GetObjectOverride(_currentGroup);
|
||||||
|
_currentScenarioOverride = NULL;
|
||||||
if (_currentObjectOverride == NULL) {
|
if (_currentObjectOverride == NULL) {
|
||||||
_objectOverrides.push_back(ObjectOverride());
|
_objectOverrides.push_back(ObjectOverride());
|
||||||
_currentObjectOverride = &_objectOverrides[_objectOverrides.size() - 1];
|
_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)
|
void LanguagePack::ParseString(IStringReader *reader)
|
||||||
{
|
{
|
||||||
auto sb = StringBuilder();
|
auto sb = StringBuilder();
|
||||||
|
@ -344,7 +438,11 @@ void LanguagePack::ParseString(IStringReader *reader)
|
||||||
|
|
||||||
_strings[stringId] = relativeOffset;
|
_strings[stringId] = relativeOffset;
|
||||||
} else {
|
} else {
|
||||||
_currentObjectOverride->strings[stringId] = relativeOffset;
|
if (_currentObjectOverride != NULL) {
|
||||||
|
_currentObjectOverride->strings[stringId] = relativeOffset;
|
||||||
|
} else {
|
||||||
|
_currentScenarioOverride->strings[stringId] = relativeOffset;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_stringDataSB.Append(sb.GetBuffer());
|
_stringDataSB.Append(sb.GetBuffer());
|
||||||
|
|
|
@ -30,6 +30,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
rct_string_id GetObjectOverrideStringId(const char *objectIdentifier, int index);
|
rct_string_id GetObjectOverrideStringId(const char *objectIdentifier, int index);
|
||||||
|
rct_string_id GetScenarioOverrideStringId(const utf8 *scenarioFilename, int index);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct ObjectOverride {
|
struct ObjectOverride {
|
||||||
|
@ -37,13 +38,27 @@ private:
|
||||||
const utf8 *strings[4];
|
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;
|
int _id;
|
||||||
utf8 *_stringData;
|
utf8 *_stringData;
|
||||||
std::vector<const utf8*> _strings;
|
std::vector<const utf8*> _strings;
|
||||||
std::vector<ObjectOverride> _objectOverrides;
|
std::vector<ObjectOverride> _objectOverrides;
|
||||||
|
std::vector<ScenarioOverride> _scenarioOverrides;
|
||||||
|
|
||||||
LanguagePack(int id, const utf8 *text);
|
LanguagePack(int id, const utf8 *text);
|
||||||
ObjectOverride *GetObjectOverride(const char *objectIdentifier);
|
ObjectOverride *GetObjectOverride(const char *objectIdentifier);
|
||||||
|
ScenarioOverride *GetScenarioOverride(const utf8 *scenarioFilename);
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
// Parsing
|
// Parsing
|
||||||
|
@ -51,9 +66,11 @@ private:
|
||||||
StringBuilder _stringDataSB;
|
StringBuilder _stringDataSB;
|
||||||
utf8 *_currentGroup;
|
utf8 *_currentGroup;
|
||||||
ObjectOverride *_currentObjectOverride;
|
ObjectOverride *_currentObjectOverride;
|
||||||
|
ScenarioOverride *_currentScenarioOverride;
|
||||||
|
|
||||||
void ParseLine(IStringReader *reader);
|
void ParseLine(IStringReader *reader);
|
||||||
void ParseGroup(IStringReader *reader);
|
void ParseGroupObject(IStringReader *reader);
|
||||||
|
void ParseGroupScenario(IStringReader *reader);
|
||||||
void ParseString(IStringReader *reader);
|
void ParseString(IStringReader *reader);
|
||||||
|
|
||||||
bool ParseToken(IStringReader *reader, uint32 *token);
|
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);
|
wchar_t *utf8_to_widechar(const utf8 *src);
|
||||||
utf8 *widechar_to_utf8(const wchar_t *src);
|
utf8 *widechar_to_utf8(const wchar_t *src);
|
||||||
|
|
||||||
|
bool language_get_localised_scenario_strings(const utf8 *scenarioFilename, rct_string_id *outStringIds);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -73,14 +73,29 @@ int scenario_load_basic(const char *path, rct_s6_header *header, rct_s6_info *in
|
||||||
SDL_RWclose(rw);
|
SDL_RWclose(rw);
|
||||||
RCT2_GLOBAL(0x009AA00C, uint8) = 0;
|
RCT2_GLOBAL(0x009AA00C, uint8) = 0;
|
||||||
|
|
||||||
// Checks for a scenario string object (possibly for localisation)
|
// Get filename
|
||||||
if ((info->entry.flags & 0xFF) != 255) {
|
utf8 filename[MAX_PATH];
|
||||||
if (object_get_scenario_text(&info->entry)) {
|
strcpy(filename, path_get_filename(path));
|
||||||
rct_stex_entry* stex_entry = RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_TEXT_TEMP_CHUNK, rct_stex_entry*);
|
path_remove_extension(filename);
|
||||||
format_string(info->name, stex_entry->scenario_name, NULL);
|
|
||||||
format_string(info->details, stex_entry->details, NULL);
|
rct_string_id localisedStringIds[3];
|
||||||
RCT2_GLOBAL(0x009AA00C, uint8) = stex_entry->var_06;
|
if (language_get_localised_scenario_strings(filename, localisedStringIds)) {
|
||||||
object_free_scenario_text();
|
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;
|
return 1;
|
||||||
|
@ -282,23 +297,45 @@ void scenario_begin()
|
||||||
strcpy((char*)RCT2_ADDRESS_SCENARIO_DETAILS, s6Info->details);
|
strcpy((char*)RCT2_ADDRESS_SCENARIO_DETAILS, s6Info->details);
|
||||||
strcpy((char*)RCT2_ADDRESS_SCENARIO_NAME, s6Info->name);
|
strcpy((char*)RCT2_ADDRESS_SCENARIO_NAME, s6Info->name);
|
||||||
|
|
||||||
rct_stex_entry* stex = g_stexEntries[0];
|
{
|
||||||
if ((int)stex != -1) {
|
// Get filename
|
||||||
char *buffer = (char*)RCT2_ADDRESS_COMMON_STRING_FORMAT_BUFFER;
|
utf8 filename[MAX_PATH];
|
||||||
|
strcpy(filename, _scenarioFileName);
|
||||||
|
path_remove_extension(filename);
|
||||||
|
|
||||||
// Set localised park name
|
rct_string_id localisedStringIds[3];
|
||||||
format_string(buffer, stex->park_name, 0);
|
if (language_get_localised_scenario_strings(filename, localisedStringIds)) {
|
||||||
park_set_name(buffer);
|
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
|
// Set localised park name
|
||||||
format_string(buffer, stex->scenario_name, 0);
|
format_string(buffer, stex->park_name, 0);
|
||||||
strncpy((char*)RCT2_ADDRESS_SCENARIO_NAME, buffer, 31);
|
park_set_name(buffer);
|
||||||
((char*)RCT2_ADDRESS_SCENARIO_NAME)[31] = '\0';
|
|
||||||
|
|
||||||
// Set localised scenario details
|
// Set localised scenario name
|
||||||
format_string(buffer, stex->details, 0);
|
format_string(buffer, stex->scenario_name, 0);
|
||||||
strncpy((char*)RCT2_ADDRESS_SCENARIO_DETAILS, buffer, 255);
|
strncpy((char*)RCT2_ADDRESS_SCENARIO_NAME, buffer, 31);
|
||||||
((char*)RCT2_ADDRESS_SCENARIO_DETAILS)[255] = '\0';
|
((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
|
// Set the last saved game path
|
||||||
|
|
Loading…
Reference in New Issue