Skip to content

Commit bb6ed0a

Browse files
committed
Gracefully handle mods quest_dialogs
1 parent 20be105 commit bb6ed0a

File tree

3 files changed

+86
-26
lines changed

3 files changed

+86
-26
lines changed

Source/townerdat.cpp

Lines changed: 61 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
#include <charconv>
99
#include <optional>
1010
#include <string_view>
11+
#include <unordered_map>
12+
#include <vector>
1113

1214
#include <expected.hpp>
1315
#include <magic_enum/magic_enum.hpp>
@@ -132,7 +134,6 @@ void LoadQuestDialogFromFile()
132134
{
133135
const std::string_view filename = "txtdata\\towners\\quest_dialog.tsv";
134136
DataFile dataFile = DataFile::loadOrDie(filename);
135-
dataFile.skipHeaderOrDie(filename);
136137

137138
// Initialize table with TEXT_NONE
138139
TownerQuestDialogTable.clear();
@@ -141,26 +142,74 @@ void LoadQuestDialogFromFile()
141142
row.fill(TEXT_NONE);
142143
}
143144

145+
// Parse header to find which quest columns exist
146+
DataFileRecord headerRecord = *dataFile.begin();
147+
std::unordered_map<std::string, unsigned> columnMap;
148+
unsigned columnIndex = 0;
149+
for (DataFileField field : headerRecord) {
150+
columnMap[std::string(field.value())] = columnIndex++;
151+
}
152+
153+
// Reset header position and skip for data reading
154+
dataFile.resetHeader();
155+
dataFile.skipHeaderOrDie(filename);
156+
157+
// Find the towner_type column index
158+
auto townerTypeColIt = columnMap.find("towner_type");
159+
if (townerTypeColIt == columnMap.end()) {
160+
return; // Invalid file format
161+
}
162+
unsigned townerTypeColIndex = townerTypeColIt->second;
163+
164+
// Build quest column index map
165+
std::unordered_map<quest_id, unsigned> questColumnMap;
166+
for (quest_id quest : magic_enum::enum_values<quest_id>()) {
167+
if (quest == Q_INVALID || quest >= MAXQUESTS) continue;
168+
169+
auto questName = magic_enum::enum_name(quest);
170+
auto questColIt = columnMap.find(std::string(questName));
171+
if (questColIt != columnMap.end()) {
172+
questColumnMap[quest] = questColIt->second;
173+
}
174+
}
175+
176+
// Read data rows
144177
for (DataFileRecord record : dataFile) {
145-
RecordReader reader { record, filename };
178+
// Read all fields into a map keyed by column index for indexed access
179+
std::unordered_map<unsigned, std::string_view> fields;
180+
for (DataFileField field : record) {
181+
fields[field.column()] = field.value();
182+
}
146183

147-
_talker_id townerType;
148-
reader.read("towner_type", townerType, ParseEnum<_talker_id>);
184+
// Read towner_type
185+
auto townerTypeFieldIt = fields.find(townerTypeColIndex);
186+
if (townerTypeFieldIt == fields.end()) {
187+
continue; // Invalid row
188+
}
189+
190+
auto townerTypeResult = ParseEnum<_talker_id>(townerTypeFieldIt->second);
191+
if (!townerTypeResult.has_value()) {
192+
continue; // Invalid towner type
193+
}
194+
_talker_id townerType = townerTypeResult.value();
149195

150196
if (static_cast<size_t>(townerType) >= TownerQuestDialogTable.size()) {
151197
continue;
152198
}
153199

154200
auto &dialogRow = TownerQuestDialogTable[static_cast<size_t>(townerType)];
155201

156-
// Read each quest column using magic_enum to iterate quest IDs
157-
for (quest_id quest : magic_enum::enum_values<quest_id>()) {
158-
if (quest == Q_INVALID || quest >= MAXQUESTS) continue;
159-
160-
auto questName = magic_enum::enum_name(quest);
161-
_speech_id speech = TEXT_NONE;
162-
reader.read(questName, speech, ParseSpeechId);
163-
dialogRow[quest] = speech;
202+
// Read quest columns that exist in this file
203+
for (const auto &[quest, colIndex] : questColumnMap) {
204+
auto fieldIt = fields.find(colIndex);
205+
if (fieldIt == fields.end()) {
206+
continue; // Column missing in this row
207+
}
208+
209+
auto speechResult = ParseSpeechId(fieldIt->second);
210+
if (speechResult.has_value()) {
211+
dialogRow[quest] = speechResult.value();
212+
}
164213
}
165214
}
166215
}
Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
1-
towner_type Q_ROCK Q_MUSHROOM Q_GARBUD Q_ZHAR Q_VEIL Q_DIABLO Q_BUTCHER Q_LTBANNER Q_BLIND Q_BLOOD Q_ANVIL Q_WARLORD Q_SKELKING Q_PWATER Q_SCHAMB Q_BETRAYER Q_GRAVE Q_FARMER Q_GIRL Q_TRADER Q_DEFILER Q_NAKRUL Q_CORNSTN Q_JERSEY
2-
TOWN_SMITH TEXT_INFRA6 TEXT_MUSH6 TEXT_NONE TEXT_NONE TEXT_VEIL5 TEXT_NONE TEXT_BUTCH5 TEXT_BANNER6 TEXT_BLIND5 TEXT_BLOOD5 TEXT_ANVIL6 TEXT_WARLRD5 TEXT_KING7 TEXT_POISON7 TEXT_BONE5 TEXT_VILE9 TEXT_GRAVE2 TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE
3-
TOWN_HEALER TEXT_INFRA3 TEXT_NONE TEXT_NONE TEXT_NONE TEXT_VEIL3 TEXT_NONE TEXT_BUTCH3 TEXT_BANNER4 TEXT_BLIND3 TEXT_BLOOD3 TEXT_ANVIL3 TEXT_WARLRD3 TEXT_KING5 TEXT_POISON4 TEXT_BONE3 TEXT_VILE7 TEXT_GRAVE3 TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE
4-
TOWN_DEADGUY TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE
5-
TOWN_TAVERN TEXT_INFRA2 TEXT_MUSH2 TEXT_NONE TEXT_NONE TEXT_VEIL2 TEXT_NONE TEXT_BUTCH2 TEXT_NONE TEXT_BLIND2 TEXT_BLOOD2 TEXT_ANVIL2 TEXT_WARLRD2 TEXT_KING3 TEXT_POISON2 TEXT_BONE2 TEXT_VILE4 TEXT_GRAVE5 TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE
6-
TOWN_STORY TEXT_INFRA1 TEXT_MUSH1 TEXT_NONE TEXT_NONE TEXT_VEIL1 TEXT_VILE3 TEXT_BUTCH1 TEXT_BANNER1 TEXT_BLIND1 TEXT_BLOOD1 TEXT_ANVIL1 TEXT_WARLRD1 TEXT_KING1 TEXT_POISON1 TEXT_BONE1 TEXT_VILE2 TEXT_GRAVE6 TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE
7-
TOWN_DRUNK TEXT_INFRA8 TEXT_MUSH7 TEXT_NONE TEXT_NONE TEXT_VEIL6 TEXT_NONE TEXT_BUTCH6 TEXT_BANNER7 TEXT_BLIND6 TEXT_BLOOD6 TEXT_ANVIL8 TEXT_WARLRD6 TEXT_KING8 TEXT_POISON8 TEXT_BONE6 TEXT_VILE10 TEXT_GRAVE7 TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE
8-
TOWN_WITCH TEXT_INFRA9 TEXT_MUSH9 TEXT_NONE TEXT_NONE TEXT_VEIL7 TEXT_NONE TEXT_BUTCH7 TEXT_BANNER8 TEXT_BLIND7 TEXT_BLOOD7 TEXT_ANVIL9 TEXT_WARLRD7 TEXT_KING9 TEXT_POISON9 TEXT_BONE7 TEXT_VILE11 TEXT_GRAVE1 TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE
9-
TOWN_BMAID TEXT_INFRA4 TEXT_MUSH5 TEXT_NONE TEXT_NONE TEXT_VEIL4 TEXT_NONE TEXT_BUTCH4 TEXT_BANNER5 TEXT_BLIND4 TEXT_BLOOD4 TEXT_ANVIL4 TEXT_WARLRD4 TEXT_KING6 TEXT_POISON6 TEXT_BONE4 TEXT_VILE8 TEXT_GRAVE8 TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE
10-
TOWN_PEGBOY TEXT_INFRA10 TEXT_MUSH13 TEXT_NONE TEXT_NONE TEXT_VEIL8 TEXT_NONE TEXT_BUTCH8 TEXT_BANNER9 TEXT_BLIND8 TEXT_BLOOD8 TEXT_ANVIL10 TEXT_WARLRD8 TEXT_KING10 TEXT_POISON10 TEXT_BONE8 TEXT_VILE12 TEXT_GRAVE9 TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE
11-
TOWN_COW TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE
12-
TOWN_FARMER TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE
13-
TOWN_GIRL TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE
14-
TOWN_COWFARM TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE
1+
towner_type Q_ROCK Q_MUSHROOM Q_GARBUD Q_ZHAR Q_VEIL Q_DIABLO Q_BUTCHER Q_LTBANNER Q_BLIND Q_BLOOD Q_ANVIL Q_WARLORD Q_SKELKING Q_PWATER Q_SCHAMB Q_BETRAYER Q_GRAVE Q_TRADER
2+
TOWN_SMITH TEXT_INFRA6 TEXT_MUSH6 TEXT_NONE TEXT_NONE TEXT_VEIL5 TEXT_NONE TEXT_BUTCH5 TEXT_BANNER6 TEXT_BLIND5 TEXT_BLOOD5 TEXT_ANVIL6 TEXT_WARLRD5 TEXT_KING7 TEXT_POISON7 TEXT_BONE5 TEXT_VILE9 TEXT_GRAVE2 TEXT_NONE
3+
TOWN_HEALER TEXT_INFRA3 TEXT_NONE TEXT_NONE TEXT_NONE TEXT_VEIL3 TEXT_NONE TEXT_BUTCH3 TEXT_BANNER4 TEXT_BLIND3 TEXT_BLOOD3 TEXT_ANVIL3 TEXT_WARLRD3 TEXT_KING5 TEXT_POISON4 TEXT_BONE3 TEXT_VILE7 TEXT_GRAVE3 TEXT_NONE
4+
TOWN_DEADGUY TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE
5+
TOWN_TAVERN TEXT_INFRA2 TEXT_MUSH2 TEXT_NONE TEXT_NONE TEXT_VEIL2 TEXT_NONE TEXT_BUTCH2 TEXT_NONE TEXT_BLIND2 TEXT_BLOOD2 TEXT_ANVIL2 TEXT_WARLRD2 TEXT_KING3 TEXT_POISON2 TEXT_BONE2 TEXT_VILE4 TEXT_GRAVE5 TEXT_NONE
6+
TOWN_STORY TEXT_INFRA1 TEXT_MUSH1 TEXT_NONE TEXT_NONE TEXT_VEIL1 TEXT_VILE3 TEXT_BUTCH1 TEXT_BANNER1 TEXT_BLIND1 TEXT_BLOOD1 TEXT_ANVIL1 TEXT_WARLRD1 TEXT_KING1 TEXT_POISON1 TEXT_BONE1 TEXT_VILE2 TEXT_GRAVE6 TEXT_NONE
7+
TOWN_DRUNK TEXT_INFRA8 TEXT_MUSH7 TEXT_NONE TEXT_NONE TEXT_VEIL6 TEXT_NONE TEXT_BUTCH6 TEXT_BANNER7 TEXT_BLIND6 TEXT_BLOOD6 TEXT_ANVIL8 TEXT_WARLRD6 TEXT_KING8 TEXT_POISON8 TEXT_BONE6 TEXT_VILE10 TEXT_GRAVE7 TEXT_NONE
8+
TOWN_WITCH TEXT_INFRA9 TEXT_MUSH9 TEXT_NONE TEXT_NONE TEXT_VEIL7 TEXT_NONE TEXT_BUTCH7 TEXT_BANNER8 TEXT_BLIND7 TEXT_BLOOD7 TEXT_ANVIL9 TEXT_WARLRD7 TEXT_KING9 TEXT_POISON9 TEXT_BONE7 TEXT_VILE11 TEXT_GRAVE1 TEXT_NONE
9+
TOWN_BMAID TEXT_INFRA4 TEXT_MUSH5 TEXT_NONE TEXT_NONE TEXT_VEIL4 TEXT_NONE TEXT_BUTCH4 TEXT_BANNER5 TEXT_BLIND4 TEXT_BLOOD4 TEXT_ANVIL4 TEXT_WARLRD4 TEXT_KING6 TEXT_POISON6 TEXT_BONE4 TEXT_VILE8 TEXT_GRAVE8 TEXT_NONE
10+
TOWN_PEGBOY TEXT_INFRA10 TEXT_MUSH13 TEXT_NONE TEXT_NONE TEXT_VEIL8 TEXT_NONE TEXT_BUTCH8 TEXT_BANNER9 TEXT_BLIND8 TEXT_BLOOD8 TEXT_ANVIL10 TEXT_WARLRD8 TEXT_KING10 TEXT_POISON10 TEXT_BONE8 TEXT_VILE12 TEXT_GRAVE9 TEXT_NONE
11+
TOWN_COW TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
towner_type Q_ROCK Q_MUSHROOM Q_GARBUD Q_ZHAR Q_VEIL Q_DIABLO Q_BUTCHER Q_LTBANNER Q_BLIND Q_BLOOD Q_ANVIL Q_WARLORD Q_SKELKING Q_PWATER Q_SCHAMB Q_BETRAYER Q_GRAVE Q_FARMER Q_GIRL Q_TRADER Q_DEFILER Q_NAKRUL Q_CORNSTN Q_JERSEY
2+
TOWN_SMITH TEXT_INFRA6 TEXT_MUSH6 TEXT_NONE TEXT_NONE TEXT_VEIL5 TEXT_NONE TEXT_BUTCH5 TEXT_BANNER6 TEXT_BLIND5 TEXT_BLOOD5 TEXT_ANVIL6 TEXT_WARLRD5 TEXT_KING7 TEXT_POISON7 TEXT_BONE5 TEXT_VILE9 TEXT_GRAVE2 TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE
3+
TOWN_HEALER TEXT_INFRA3 TEXT_NONE TEXT_NONE TEXT_NONE TEXT_VEIL3 TEXT_NONE TEXT_BUTCH3 TEXT_BANNER4 TEXT_BLIND3 TEXT_BLOOD3 TEXT_ANVIL3 TEXT_WARLRD3 TEXT_KING5 TEXT_POISON4 TEXT_BONE3 TEXT_VILE7 TEXT_GRAVE3 TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE
4+
TOWN_DEADGUY TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE
5+
TOWN_TAVERN TEXT_INFRA2 TEXT_MUSH2 TEXT_NONE TEXT_NONE TEXT_VEIL2 TEXT_NONE TEXT_BUTCH2 TEXT_NONE TEXT_BLIND2 TEXT_BLOOD2 TEXT_ANVIL2 TEXT_WARLRD2 TEXT_KING3 TEXT_POISON2 TEXT_BONE2 TEXT_VILE4 TEXT_GRAVE5 TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE
6+
TOWN_STORY TEXT_INFRA1 TEXT_MUSH1 TEXT_NONE TEXT_NONE TEXT_VEIL1 TEXT_VILE3 TEXT_BUTCH1 TEXT_BANNER1 TEXT_BLIND1 TEXT_BLOOD1 TEXT_ANVIL1 TEXT_WARLRD1 TEXT_KING1 TEXT_POISON1 TEXT_BONE1 TEXT_VILE2 TEXT_GRAVE6 TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE
7+
TOWN_DRUNK TEXT_INFRA8 TEXT_MUSH7 TEXT_NONE TEXT_NONE TEXT_VEIL6 TEXT_NONE TEXT_BUTCH6 TEXT_BANNER7 TEXT_BLIND6 TEXT_BLOOD6 TEXT_ANVIL8 TEXT_WARLRD6 TEXT_KING8 TEXT_POISON8 TEXT_BONE6 TEXT_VILE10 TEXT_GRAVE7 TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE
8+
TOWN_WITCH TEXT_INFRA9 TEXT_MUSH9 TEXT_NONE TEXT_NONE TEXT_VEIL7 TEXT_NONE TEXT_BUTCH7 TEXT_BANNER8 TEXT_BLIND7 TEXT_BLOOD7 TEXT_ANVIL9 TEXT_WARLRD7 TEXT_KING9 TEXT_POISON9 TEXT_BONE7 TEXT_VILE11 TEXT_GRAVE1 TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE
9+
TOWN_BMAID TEXT_INFRA4 TEXT_MUSH5 TEXT_NONE TEXT_NONE TEXT_VEIL4 TEXT_NONE TEXT_BUTCH4 TEXT_BANNER5 TEXT_BLIND4 TEXT_BLOOD4 TEXT_ANVIL4 TEXT_WARLRD4 TEXT_KING6 TEXT_POISON6 TEXT_BONE4 TEXT_VILE8 TEXT_GRAVE8 TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE
10+
TOWN_PEGBOY TEXT_INFRA10 TEXT_MUSH13 TEXT_NONE TEXT_NONE TEXT_VEIL8 TEXT_NONE TEXT_BUTCH8 TEXT_BANNER9 TEXT_BLIND8 TEXT_BLOOD8 TEXT_ANVIL10 TEXT_WARLRD8 TEXT_KING10 TEXT_POISON10 TEXT_BONE8 TEXT_VILE12 TEXT_GRAVE9 TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE
11+
TOWN_COW TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE
12+
TOWN_FARMER TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE
13+
TOWN_GIRL TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE
14+
TOWN_COWFARM TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE TEXT_NONE

0 commit comments

Comments
 (0)