18 if (std::filesystem::file_size(
path) > 16U * 1024U * 1024U)
19 throw std::runtime_error(
"text input exceeds 16 MiB: " +
path.string());
20 std::ifstream stream(
path, std::ios::binary);
21 if (!stream)
throw std::runtime_error(
"cannot open " +
path.string());
22 std::string
text{std::istreambuf_iterator<char>(stream), {}};
23 if (stream.bad())
throw std::runtime_error(
"cannot read " +
path.string());
24 for (
unsigned char c :
text)
25 if (
c == 0 ||
c > 127)
34 [[
noreturn]]
void fail(std::string message)
const
35 {
throw std::runtime_error(
"ATR byte " + std::to_string(offset) +
": " + message); }
38 while (offset <
text.size())
40 if (text[offset] ==
' ' || text[offset] ==
'\t' ||
41 text[offset] ==
'\r' || text[offset] ==
'\n') { ++
offset;
continue; }
42 if (
text.substr(offset, 2) ==
"//")
44 const auto end =
text.find(
'\n', offset + 2);
45 offset = end == std::string_view::npos ?
text.size() : end + 1;
47 else if (
text.substr(offset, 2) ==
"/*")
49 const auto end =
text.find(
"*/", offset + 2);
50 if (end == std::string_view::npos) fail(
"unterminated comment");
60 while (offset <
text.size())
63 if (!((
c >=
'A' &&
c <=
'Z') || (
c >=
'a' &&
c <=
'z') ||
64 (
c >=
'0' &&
c <=
'9') ||
c ==
'_' ||
c ==
'.'))
break;
67 if (start == offset) fail(
"expected a token");
68 return std::string(
text.substr(start, offset - start));
73 if (offset ==
text.size() || text[offset++] !=
'{') fail(
"expected message brace");
75 const auto end =
text.find(
'}', start);
76 if (end == std::string_view::npos) fail(
"unterminated message");
78 if (
nested != std::string_view::npos &&
nested < end) fail(
"nested message brace");
81 return std::string(
text.substr(start, end - start));
86 out.push_back(
static_cast<std::byte
>(
value & 255U));
87 out.push_back(
static_cast<std::byte
>(
value >> 8U));
94 const std::filesystem::path&
atr)
97 std::map<std::string, std::uint32_t>
symbols;
101 const auto extras =
generated.find(
"/* Extra stuff (not automatically generated)");
105 while (std::getline(
header, line))
107 std::istringstream
tokens(line);
111 throw std::runtime_error(
"malformed language definition: " + line);
116 throw std::runtime_error(
"non-literal language definition: " +
name);
118 throw std::runtime_error(
"duplicate language definition: " +
name);
121 if (
symbols.empty())
throw std::runtime_error(
"language definitions are empty");
124 if (reader.word() !=
"_32Bit" || reader.word() !=
"_32Bit" ||
125 reader.word() !=
"_String" || reader.word() !=
"252")
126 reader.fail(
"unsupported ATR schema; expected two 32-bit fields and String 252");
127 std::set<std::string>
seen;
131 if (reader.offset ==
source.size())
break;
132 if (reader.word() !=
".ITEM") reader.fail(
"expected .ITEM");
133 const auto symbol = reader.word();
134 const auto sound = reader.word();
135 auto message = reader.message();
136 if (!
seen.insert(symbol).second) reader.fail(
"duplicate message " + symbol);
137 if (sound !=
"WAV_NO_SOUND")
138 reader.fail(
"audio mapping requires a separate supplied sound bank: " + sound);
146 if (!
inserted && item->second != message)
147 reader.fail(
"conflicting aliases for language ID " + std::to_string(
found->second));
154 if (
input.text.empty())
return {};
156 throw std::runtime_error(
"language strings exceed DataTag capacity");
157 std::vector<ArchiveBuildItem> items(1);
159 for (
const auto& [
id, message] :
input.text)
161 u32(items[0].payload,
id);
162 u16(items[0].payload,
static_cast<std::uint16_t
>(items.size()));
std::shared_ptr< const data::HmdTextureImage > source
static constexpr std::size_t MaximumItemCount
EnglishText readEnglishText(const std::filesystem::path &definitions, const std::filesystem::path &atr)
std::vector< ArchiveBuildItem > buildEnglishItems(const EnglishText &input)
std::vector< std::byte > DataBytes
std::map< std::uint32_t, std::string > requiredNames
std::vector< std::string > unmappedSymbols
std::map< std::uint32_t, std::string > text