-
Notifications
You must be signed in to change notification settings - Fork 77
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1988 from momoko-h/IEDT
Improve loading strings from EDT files
- Loading branch information
Showing
18 changed files
with
296 additions
and
107 deletions.
There are no files selected for viewing
4 changes: 4 additions & 0 deletions
4
assets/mods/test-json-dialogs/data/binarydata/aimbios.edt.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"0": { "1": "This is the additional info for Barry who usually does not have any. The above text still comes from the original aimbios.edt file. Please note that this text is longer than the old limit of 160 characters." }, | ||
"5": { "0": "Main description for Trevor. Unicode seems to work partially (it depends on the font, of course): ô£ÇБÿÄщµ¢ć. Error messages about invalid characters in the console are expected." } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"0": { "0": "This is the footer of the help screen." } | ||
} |
6 changes: 6 additions & 0 deletions
6
assets/mods/test-json-dialogs/data/binarydata/mercbios.edt.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
{ | ||
"0": { | ||
"0": "This is Biff's description.", | ||
"1": "This is Biff's additional information." | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
{ | ||
"name": "Test JSON dialogs", | ||
"description": "This mod replaces all of Igor's dialog quotes with 'Test quote XXX'", | ||
"version": "1.0.0" | ||
"name": "Test JSON edt replacements", | ||
"description": "This is a development helper mod that replaces all of Igor's dialog quotes with 'Test quote XXX' as well as several other srings stored in .edt files. End users should not enable this mod.", | ||
"version": "2.0.2" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
#include "ContentManager.h" | ||
#include "DefaultContentManager.h" | ||
#include "EncryptedString.h" | ||
#include "IEDT.h" | ||
#include "Json.h" | ||
#include "ModPackContentManager.h" | ||
#include "SGPFile.h" | ||
#include <charconv> | ||
#include <memory> | ||
#include <numeric> | ||
#include <optional> | ||
#include <string_view> | ||
#include <vector> | ||
#include "string_theory/string" | ||
|
||
namespace { | ||
|
||
class ClassicEDT final : public IEDT | ||
{ | ||
std::unique_ptr<SGPFile> underlyingFile; | ||
std::vector<IEDT::column_list::value_type> columns; | ||
unsigned rowLength; | ||
|
||
public: | ||
ClassicEDT(ContentManager const& cm, | ||
std::string_view const filename, | ||
IEDT::column_list columnsList) : | ||
underlyingFile{ cm.openGameResForReading(filename) }, | ||
columns{ columnsList }, | ||
rowLength{ std::accumulate(columns.begin(), columns.end(), 0U) } | ||
{ | ||
} | ||
|
||
ST::string at(unsigned const row, unsigned const column) const override | ||
{ | ||
return LoadEncryptedString(underlyingFile.get(), | ||
row * rowLength + std::accumulate | ||
(columns.begin(), columns.begin() + column, 0U), | ||
columns.at(column)); | ||
} | ||
|
||
~ClassicEDT() override = default; | ||
}; | ||
|
||
|
||
class JsonEDT final : public IEDT | ||
{ | ||
template<std::size_t TN> | ||
static char * conv(char (&buf)[TN], unsigned const value) | ||
{ | ||
auto const convResult{ std::to_chars(buf, buf + TN, value) }; | ||
// to_chars does not NUL terminate its result, fix that here. | ||
*convResult.ptr = 0; | ||
return buf; | ||
} | ||
|
||
std::optional<ST::string> attemptToGet(unsigned const row, unsigned const column) const | ||
{ | ||
char buf[64]; | ||
if (jsonObject.has(conv(buf, row))) | ||
{ | ||
auto const rowObject{ jsonObject.GetValue(buf).toObject() }; | ||
if (rowObject.has(conv(buf, column))) | ||
{ | ||
return rowObject.GetString(buf); | ||
} | ||
} | ||
|
||
return std::nullopt; | ||
} | ||
|
||
ClassicEDT backupEDT; | ||
JsonObject jsonObject; | ||
|
||
public: | ||
JsonEDT(ContentManager const& cm, | ||
std::string_view const filename, | ||
IEDT::column_list columnsList) | ||
: backupEDT{ cm, filename, columnsList } | ||
{ | ||
auto const jsonFilename{ ST::string{filename} + ".json" }; | ||
if (cm.doesGameResExists(jsonFilename)) | ||
{ | ||
std::unique_ptr<SGPFile> jsonfile{ cm.openGameResForReading(jsonFilename) }; | ||
jsonObject = JsonValue::deserialize(jsonfile->readStringToEnd()).toObject(); | ||
} | ||
} | ||
|
||
ST::string at(unsigned const row, unsigned const column) const override | ||
{ | ||
auto const result{ attemptToGet(row, column) }; | ||
// Not using result.value_or here because that would make us | ||
// always call backupEDT.at(). | ||
return result ? *result : backupEDT.at(row, column); | ||
} | ||
|
||
~JsonEDT() override = default; | ||
}; | ||
} // anonymous namespace | ||
|
||
|
||
IEDT::uptr DefaultContentManager::openEDT(std::string_view const filename, | ||
IEDT::column_list columns) const | ||
{ | ||
return IEDT::uptr{ new ClassicEDT(*this, filename, columns) }; | ||
} | ||
|
||
|
||
IEDT::uptr ModPackContentManager::openEDT(std::string_view const filename, | ||
IEDT::column_list columns) const | ||
{ | ||
return IEDT::uptr{ new JsonEDT(*this, filename, columns) }; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
#pragma once | ||
|
||
#include "ContentManager.h" | ||
#include "Directories.h" | ||
#include "GameInstance.h" | ||
#include "IEDT.h" | ||
#include <string_view> | ||
|
||
/* | ||
EDTFile: support class for reading strings from EDT files. | ||
EDT files are essentially just one big blob of slightly obfuscated | ||
UTF-16 (or UCS2?) characters. They contain absolutely no metadata to | ||
indicate where one string ends and the next one begins. It is entirely | ||
up to the code reading the file to break up the blob into single strings. | ||
This interpretation is done in two ways in JA2: | ||
- The entire file is seen as an array of fixed width strings. | ||
- The file is seen as a table where each row has the same column layout. | ||
The first case could also be interpreted as a table where each row has | ||
just one column. To keep things simple, the IEDT interface treats all EDT | ||
files as tables. | ||
The default content manager uses the IEDT implementation provided by the | ||
class ClassicEDT. This implementation can only read files in the original | ||
.edt file format. It does not do much; it simply opens the file and | ||
calculates the file offset from the row and column number. | ||
The mod content manager uses the IEDT implementation provided by the class | ||
JsonEDT, where each table entry can be provided by a JSON file. The file | ||
name of this file is the original file name with .json appended. Strings | ||
that are not found in the JSON file are taken from an original .edt file | ||
as a fallback option. | ||
As already mentioned, EDT contains no metadata, therefore ClassicEDT | ||
requires some details about each individual file. These details are | ||
contained in the EDTFilesTable below which also shows which files can | ||
currently be overriden. | ||
To support a new file add an entry to the table then change the source | ||
file to use EDTFile instead of ContentManager::loadEncryptedString. | ||
*/ | ||
|
||
class EDTFile | ||
{ | ||
struct EDTFilesTable | ||
{ | ||
std::string_view filename; | ||
IEDT::column_list columns; | ||
}; | ||
|
||
static constexpr EDTFilesTable EDTFilesTable[] | ||
{ | ||
/* Description strings of the A.I.M. members screen. | ||
One row per merc (40 in total) with two columns each: | ||
Column 0: Long description (original limit 400 characters) | ||
Column 1: Additional information (160 characters) | ||
*/ | ||
{ BINARYDATADIR "/aimbios.edt", { 400, 160 } }, | ||
|
||
/* Description strings of the M.E.R.C. files. | ||
One row per merc (10 in total) with two columns each: | ||
Column 0: Long description (original limit 400 characters) | ||
Column 1: Additional information (160 characters) | ||
*/ | ||
{ BINARYDATADIR "/mercbios.edt", { 400, 160 } }, | ||
|
||
/* Strings of the help screen. | ||
123 rows, each with one column of 640 characters. | ||
See HelpScreenText.h to get a rough overview of the row contents. | ||
*/ | ||
{ BINARYDATADIR "/help.edt", { 640 } }, | ||
}; | ||
|
||
IEDT::uptr mIEDT; | ||
|
||
public: | ||
enum EDTFilesList | ||
{ | ||
AIMBIOS, | ||
MERCBIOS, | ||
HELP | ||
}; | ||
|
||
EDTFile(EDTFilesList const whichFile) | ||
{ | ||
auto const& fileMetaData{ EDTFilesTable[whichFile] }; | ||
mIEDT = GCM->openEDT(fileMetaData.filename, fileMetaData.columns); | ||
} | ||
|
||
auto at(unsigned const row, unsigned const column = 0) const | ||
{ | ||
return mIEDT->at(row, column); | ||
} | ||
}; |
Oops, something went wrong.