1 #include "building/type.h"
2 #include "core/lang.h"
3
4 #include "core/buffer.h"
5 #include "core/file.h"
6 #include "core/io.h"
7 #include "core/log.h"
8 #include "core/string.h"
9 #include "scenario/building.h"
10 #include "translation/translation.h"
11
12 #include <stdlib.h>
13 #include <string.h>
14
15 #define MAX_TEXT_ENTRIES 1000
16 #define MAX_TEXT_DATA 200000
17 #define MIN_TEXT_SIZE (28 + MAX_TEXT_ENTRIES * 8)
18 #define MAX_TEXT_SIZE (MIN_TEXT_SIZE + MAX_TEXT_DATA)
19
20 #define MAX_MESSAGE_ENTRIES 400
21 #define MAX_MESSAGE_DATA 460000
22 #define MIN_MESSAGE_SIZE 32024
23 #define MAX_MESSAGE_SIZE (MIN_MESSAGE_SIZE + MAX_MESSAGE_DATA)
24
25 #define BUFFER_SIZE 400000
26
27 #define FILE_TEXT_ENG "c3.eng"
28 #define FILE_MM_ENG "c3_mm.eng"
29 #define FILE_TEXT_RUS "c3.rus"
30 #define FILE_MM_RUS "c3_mm.rus"
31 #define FILE_EDITOR_TEXT_ENG "c3_map.eng"
32 #define FILE_EDITOR_MM_ENG "c3_map_mm.eng"
33
34 static struct {
35 struct {
36 int32_t offset;
37 int32_t in_use;
38 } text_entries[MAX_TEXT_ENTRIES];
39 uint8_t text_data[MAX_TEXT_DATA];
40
41 lang_message message_entries[MAX_MESSAGE_ENTRIES];
42 uint8_t message_data[MAX_MESSAGE_DATA];
43 } data;
44
file_exists_in_dir(const char * dir,const char * file)45 static int file_exists_in_dir(const char *dir, const char *file)
46 {
47 char path[2 * FILE_NAME_MAX];
48 path[2 * FILE_NAME_MAX - 1] = 0;
49 strncpy(path, dir, 2 * FILE_NAME_MAX - 1);
50 strncat(path, "/", 2 * FILE_NAME_MAX - 1);
51 strncat(path, file, 2 * FILE_NAME_MAX - 1);
52 return file_exists(path, NOT_LOCALIZED);
53 }
54
lang_dir_is_valid(const char * dir)55 int lang_dir_is_valid(const char *dir)
56 {
57 if (file_exists_in_dir(dir, FILE_TEXT_ENG) && file_exists_in_dir(dir, FILE_MM_ENG)) {
58 return 1;
59 }
60 if (file_exists_in_dir(dir, FILE_TEXT_RUS) && file_exists_in_dir(dir, FILE_MM_RUS)) {
61 return 1;
62 }
63 return 0;
64 }
65
parse_text(buffer * buf)66 static void parse_text(buffer *buf)
67 {
68 buffer_skip(buf, 28); // header
69 for (int i = 0; i < MAX_TEXT_ENTRIES; i++) {
70 data.text_entries[i].offset = buffer_read_i32(buf);
71 data.text_entries[i].in_use = buffer_read_i32(buf);
72 }
73 buffer_read_raw(buf, data.text_data, MAX_TEXT_DATA);
74 }
75
load_text(const char * filename,int localizable,uint8_t * buf_data)76 static int load_text(const char *filename, int localizable, uint8_t *buf_data)
77 {
78 buffer buf;
79 int filesize = io_read_file_into_buffer(filename, localizable, buf_data, BUFFER_SIZE);
80 if (filesize < MIN_TEXT_SIZE || filesize > MAX_TEXT_SIZE) {
81 return 0;
82 }
83 buffer_init(&buf, buf_data, filesize);
84 parse_text(&buf);
85 return 1;
86 }
87
get_message_text(int32_t offset)88 static uint8_t *get_message_text(int32_t offset)
89 {
90 if (!offset) {
91 return 0;
92 }
93 return &data.message_data[offset];
94 }
95
parse_message(buffer * buf)96 static void parse_message(buffer *buf)
97 {
98 buffer_skip(buf, 24); // header
99 for (int i = 0; i < MAX_MESSAGE_ENTRIES; i++) {
100 lang_message *m = &data.message_entries[i];
101 m->type = buffer_read_i16(buf);
102 m->message_type = buffer_read_i16(buf);
103 buffer_skip(buf, 2);
104 m->x = buffer_read_i16(buf);
105 m->y = buffer_read_i16(buf);
106 m->width_blocks = buffer_read_i16(buf);
107 m->height_blocks = buffer_read_i16(buf);
108 m->image.id = buffer_read_i16(buf);
109 m->image.x = buffer_read_i16(buf);
110 m->image.y = buffer_read_i16(buf);
111 buffer_skip(buf, 6); // unused image2 id, x, y
112 m->title.x = buffer_read_i16(buf);
113 m->title.y = buffer_read_i16(buf);
114 m->subtitle.x = buffer_read_i16(buf);
115 m->subtitle.y = buffer_read_i16(buf);
116 buffer_skip(buf, 4);
117 m->video.x = buffer_read_i16(buf);
118 m->video.y = buffer_read_i16(buf);
119 buffer_skip(buf, 14);
120 m->urgent = buffer_read_i32(buf);
121
122 m->video.text = get_message_text(buffer_read_i32(buf));
123 buffer_skip(buf, 4);
124 m->title.text = get_message_text(buffer_read_i32(buf));
125 m->subtitle.text = get_message_text(buffer_read_i32(buf));
126 m->content.text = get_message_text(buffer_read_i32(buf));
127 }
128 buffer_read_raw(buf, &data.message_data, MAX_MESSAGE_DATA);
129 }
130
131
set_message_parameters(lang_message * m,int title,int text,int urgent,int message_type)132 static void set_message_parameters(lang_message *m, int title, int text, int urgent, int message_type)
133 {
134 m->type = TYPE_MESSAGE;
135 m->message_type = message_type;
136 m->x = 0;
137 m->y = 0;
138 m->width_blocks = 30;
139 m->height_blocks = 20;
140 m->title.x = 0;
141 m->title.y = 0;
142 m->urgent = urgent;
143
144 m->title.text = translation_for(title);
145 m->content.text = translation_for(text);
146 }
147
148
load_custom_messages(void)149 void load_custom_messages(void)
150 {
151 int i = 321;
152 while (i < MAX_MESSAGE_ENTRIES) {
153 if (!data.message_entries[i].content.text) {
154 break;
155 }
156 i++;
157 }
158
159 if (i >= MAX_MESSAGE_ENTRIES) {
160 log_error("Message entry max exceeded", "", 0);
161 return;
162 }
163
164 // soldiers starving
165 lang_message *m = &data.message_entries[i];
166 set_message_parameters(m, TR_CITY_MESSAGE_TITLE_MESS_HALL_NEEDS_FOOD, TR_CITY_MESSAGE_TEXT_MESS_HALL_NEEDS_FOOD, 1,
167 MESSAGE_TYPE_GENERAL);
168 m->video.text = (uint8_t *) "smk/god_mars.smk";
169 i += 1;
170
171 // soldiers starving, no mess hall
172 m = &data.message_entries[i];
173 set_message_parameters(m, TR_CITY_MESSAGE_TITLE_MESS_HALL_NEEDS_FOOD, TR_CITY_MESSAGE_TEXT_MESS_HALL_MISSING, 1,
174 MESSAGE_TYPE_GENERAL);
175 i += 1;
176
177 // monument completed
178 m = &data.message_entries[i];
179 set_message_parameters(m, TR_CITY_MESSAGE_TITLE_GRAND_TEMPLE_COMPLETE, TR_CITY_MESSAGE_TEXT_GRAND_TEMPLE_COMPLETE, 0,
180 MESSAGE_TYPE_BUILDING_COMPLETION);
181 i += 1;
182
183 // replacement Mercury blessing
184 m = &data.message_entries[i];
185 set_message_parameters(m, TR_CITY_MESSAGE_TITLE_MERCURY_BLESSING, TR_CITY_MESSAGE_TEXT_MERCURY_BLESSING, 0,
186 MESSAGE_TYPE_GENERAL);
187 i += 1;
188
189 // auto festivals
190 m = &data.message_entries[i];
191 set_message_parameters(m, TR_CITY_MESSAGE_TITLE_PANTHEON_FESTIVAL, TR_CITY_MESSAGE_TEXT_PANTHEON_FESTIVAL_CERES, 0,
192 MESSAGE_TYPE_GENERAL);
193 i += 1;
194
195 m = &data.message_entries[i];
196 set_message_parameters(m, TR_CITY_MESSAGE_TITLE_PANTHEON_FESTIVAL, TR_CITY_MESSAGE_TEXT_PANTHEON_FESTIVAL_NEPTUNE, 0,
197 MESSAGE_TYPE_GENERAL);
198 i += 1;
199
200 m = &data.message_entries[i];
201 set_message_parameters(m, TR_CITY_MESSAGE_TITLE_PANTHEON_FESTIVAL, TR_CITY_MESSAGE_TEXT_PANTHEON_FESTIVAL_MERCURY, 0,
202 MESSAGE_TYPE_GENERAL);
203 i += 1;
204
205 m = &data.message_entries[i];
206 set_message_parameters(m, TR_CITY_MESSAGE_TITLE_PANTHEON_FESTIVAL, TR_CITY_MESSAGE_TEXT_PANTHEON_FESTIVAL_MARS, 0,
207 MESSAGE_TYPE_GENERAL);
208 i += 1;
209
210 m = &data.message_entries[i];
211 set_message_parameters(m, TR_CITY_MESSAGE_TITLE_PANTHEON_FESTIVAL, TR_CITY_MESSAGE_TEXT_PANTHEON_FESTIVAL_VENUS, 0,
212 MESSAGE_TYPE_GENERAL);
213 i += 1;
214
215 m = &data.message_entries[i];
216 set_message_parameters(m, TR_CITY_MESSAGE_TITLE_MONUMENT_COMPLETE, TR_CITY_MESSAGE_TEXT_PANTHEON_COMPLETE, 0,
217 MESSAGE_TYPE_GENERAL);
218 i += 1;
219
220 m = &data.message_entries[i];
221 set_message_parameters(m, TR_CITY_MESSAGE_TITLE_MONUMENT_COMPLETE, TR_CITY_MESSAGE_TEXT_LIGHTHOUSE_COMPLETE, 0,
222 MESSAGE_TYPE_GENERAL);
223 i += 1;
224
225 m = &data.message_entries[i];
226 set_message_parameters(m, TR_CITY_MESSAGE_TITLE_NEPTUNE_BLESSING, TR_CITY_MESSAGE_TEXT_NEPTUNE_BLESSING, 0,
227 MESSAGE_TYPE_GENERAL);
228 i += 1;
229
230 m = &data.message_entries[i];
231 set_message_parameters(m, TR_CITY_MESSAGE_TITLE_VENUS_BLESSING, TR_CITY_MESSAGE_TEXT_VENUS_BLESSING, 0,
232 MESSAGE_TYPE_GENERAL);
233 i += 1;
234
235 m = &data.message_entries[i];
236 set_message_parameters(m, TR_CITY_MESSAGE_TITLE_MONUMENT_COMPLETE, TR_CITY_MESSAGE_TEXT_COLOSSEUM_COMPLETE, 0,
237 MESSAGE_TYPE_GENERAL);
238 i += 1;
239
240 m = &data.message_entries[i];
241 set_message_parameters(m, TR_CITY_MESSAGE_TITLE_MONUMENT_COMPLETE, TR_CITY_MESSAGE_TEXT_HIPPODROME_COMPLETE, 0,
242 MESSAGE_TYPE_GENERAL);
243 i += 1;
244
245 m = &data.message_entries[i];
246 set_message_parameters(m, TR_CITY_MESSAGE_TITLE_COLOSSEUM_WORKING, TR_CITY_MESSAGE_TEXT_COLOSSEUM_WORKING, 1,
247 MESSAGE_TYPE_GENERAL);
248 m->video.text = (uint8_t *) "smk/1ST_GLAD.smk";
249 i += 1;
250
251 m = &data.message_entries[i];
252 set_message_parameters(m, TR_CITY_MESSAGE_TITLE_HIPPODROME_WORKING, TR_CITY_MESSAGE_TEXT_HIPPODROME_WORKING, 1,
253 MESSAGE_TYPE_GENERAL);
254 m->video.text = (uint8_t *) "smk/1st_Chariot.smk";
255 i += 1;
256
257 for (int j = 0; j < 12; ++j) {
258 m = &data.message_entries[i];
259 set_message_parameters(m, TR_CITY_MESSAGE_TITLE_GREAT_GAMES, TR_CITY_MESSAGE_TEXT_NAVAL_GAMES_PLANNING + j, 1,
260 MESSAGE_TYPE_GENERAL);
261 i += 1;
262 }
263
264 m = &data.message_entries[i];
265 set_message_parameters(m, TR_CITY_MESSAGE_TITLE_LOOTING, TR_CITY_MESSAGE_TEXT_LOOTING, 1, MESSAGE_TYPE_DISASTER);
266 i += 1;
267
268 for (int j = 0; j < 3; ++j) {
269 m = &data.message_entries[i];
270 set_message_parameters(m, TR_CITY_MESSAGE_TITLE_GREAT_GAMES, TR_CITY_MESSAGE_TEXT_IMPERIAL_GAMES_PLANNING + j, 1,
271 MESSAGE_TYPE_GENERAL);
272 i += 1;
273 }
274 }
275
276
load_message(const char * filename,int localizable,uint8_t * data_buffer)277 static int load_message(const char *filename, int localizable, uint8_t *data_buffer)
278 {
279 buffer buf;
280 int filesize = io_read_file_into_buffer(filename, localizable, data_buffer, BUFFER_SIZE);
281 if (filesize < MIN_MESSAGE_SIZE || filesize > MAX_MESSAGE_SIZE) {
282 return 0;
283 }
284 buffer_init(&buf, data_buffer, filesize);
285 parse_message(&buf);
286 return 1;
287 }
288
load_files(const char * text_filename,const char * message_filename,int localizable)289 static int load_files(const char *text_filename, const char *message_filename, int localizable)
290 {
291 uint8_t *buffer = (uint8_t *) malloc(BUFFER_SIZE);
292 if (!buffer) {
293 return 0;
294 }
295 int success = load_text(text_filename, localizable, buffer) && load_message(message_filename, localizable, buffer);
296 free(buffer);
297 return success;
298 }
299
lang_load(int is_editor)300 int lang_load(int is_editor)
301 {
302 if (is_editor) {
303 return load_files(FILE_EDITOR_TEXT_ENG, FILE_EDITOR_MM_ENG, MAY_BE_LOCALIZED);
304 }
305 // Prefer language files from localized dir, fall back to main dir
306 return
307 load_files(FILE_TEXT_ENG, FILE_MM_ENG, MUST_BE_LOCALIZED) ||
308 load_files(FILE_TEXT_RUS, FILE_MM_RUS, MUST_BE_LOCALIZED) ||
309 load_files(FILE_TEXT_ENG, FILE_MM_ENG, NOT_LOCALIZED) ||
310 load_files(FILE_TEXT_RUS, FILE_MM_RUS, NOT_LOCALIZED);
311 }
312
lang_get_string(int group,int index)313 const uint8_t *lang_get_string(int group, int index)
314 {
315 if (group == CUSTOM_TRANSLATION) {
316 return translation_for(index);
317 }
318 if (group == 92 && !index) {
319 return translation_for(TR_BUILDING_SMALL_TEMPLE_CERES_NAME);
320 }
321 if (group == 93 && !index) {
322 return translation_for(TR_BUILDING_SMALL_TEMPLE_NEPTUNE_NAME);
323 }
324 if (group == 94 && !index) {
325 return translation_for(TR_BUILDING_SMALL_TEMPLE_MERCURY_NAME);
326 }
327 if (group == 95 && !index) {
328 return translation_for(TR_BUILDING_SMALL_TEMPLE_MARS_NAME);
329 }
330 if (group == 96 && !index) {
331 return translation_for(TR_BUILDING_SMALL_TEMPLE_VENUS_NAME);
332 }
333 if (group == 23 && index == 6 && scenario_building_allowed(BUILDING_WHARF)) {
334 return translation_for(TR_RESOURCE_FISH);
335 }
336
337 if (group == 130) {
338 switch (index) {
339 case 641:
340 return translation_for(TR_PHRASE_FIGURE_MISSIONARY_EXACT_4);
341 default:
342 break;
343 }
344 }
345
346 if (group == 67 && index == 48) {
347 return translation_for(TR_EDITOR_ALLOWED_BUILDINGS_MONUMENTS);
348 }
349
350 // Building strings
351 if (group == 28 || group == 41) {
352 switch (index) {
353 case BUILDING_ROADBLOCK:
354 return translation_for(TR_BUILDING_ROADBLOCK);
355 case BUILDING_WORKCAMP:
356 return translation_for(TR_BUILDING_WORK_CAMP);
357 case BUILDING_GRAND_TEMPLE_CERES:
358 return translation_for(TR_BUILDING_GRAND_TEMPLE_CERES);
359 case BUILDING_GRAND_TEMPLE_NEPTUNE:
360 return translation_for(TR_BUILDING_GRAND_TEMPLE_NEPTUNE);
361 case BUILDING_GRAND_TEMPLE_MERCURY:
362 return translation_for(TR_BUILDING_GRAND_TEMPLE_MERCURY);
363 case BUILDING_GRAND_TEMPLE_MARS:
364 return translation_for(TR_BUILDING_GRAND_TEMPLE_MARS);
365 case BUILDING_GRAND_TEMPLE_VENUS:
366 return translation_for(TR_BUILDING_GRAND_TEMPLE_VENUS);
367 case BUILDING_PANTHEON:
368 return translation_for(TR_BUILDING_PANTHEON);
369 case BUILDING_MENU_GRAND_TEMPLES:
370 return translation_for(TR_BUILDING_GRAND_TEMPLE_MENU);
371 case BUILDING_ARCHITECT_GUILD:
372 return translation_for(TR_BUILDING_ARCHITECT_GUILD);
373 case BUILDING_MESS_HALL:
374 return translation_for(TR_BUILDING_MESS_HALL);
375 case BUILDING_MENU_TREES:
376 return translation_for(TR_BUILDING_MENU_TREES);
377 case BUILDING_MENU_PATHS:
378 return translation_for(TR_BUILDING_MENU_PATHS);
379 case BUILDING_MENU_PARKS:
380 return translation_for(TR_BUILDING_MENU_PARKS);
381 case BUILDING_SMALL_POND:
382 return translation_for(TR_BUILDING_SMALL_POND);
383 case BUILDING_LARGE_POND:
384 return translation_for(TR_BUILDING_LARGE_POND);
385 case BUILDING_PINE_TREE:
386 return translation_for(TR_BUILDING_PINE_TREE);
387 case BUILDING_FIR_TREE:
388 return translation_for(TR_BUILDING_FIR_TREE);
389 case BUILDING_OAK_TREE:
390 return translation_for(TR_BUILDING_OAK_TREE);
391 case BUILDING_ELM_TREE:
392 return translation_for(TR_BUILDING_ELM_TREE);
393 case BUILDING_FIG_TREE:
394 return translation_for(TR_BUILDING_FIG_TREE);
395 case BUILDING_PLUM_TREE:
396 return translation_for(TR_BUILDING_PLUM_TREE);
397 case BUILDING_PALM_TREE:
398 return translation_for(TR_BUILDING_PALM_TREE);
399 case BUILDING_DATE_TREE:
400 return translation_for(TR_BUILDING_DATE_TREE);
401 case BUILDING_PINE_PATH:
402 return translation_for(TR_BUILDING_PINE_PATH);
403 case BUILDING_FIR_PATH:
404 return translation_for(TR_BUILDING_FIR_PATH);
405 case BUILDING_OAK_PATH:
406 return translation_for(TR_BUILDING_OAK_PATH);
407 case BUILDING_ELM_PATH:
408 return translation_for(TR_BUILDING_ELM_PATH);
409 case BUILDING_FIG_PATH:
410 return translation_for(TR_BUILDING_FIG_PATH);
411 case BUILDING_PLUM_PATH:
412 return translation_for(TR_BUILDING_PLUM_PATH);
413 case BUILDING_PALM_PATH:
414 return translation_for(TR_BUILDING_PALM_PATH);
415 case BUILDING_DATE_PATH:
416 return translation_for(TR_BUILDING_DATE_PATH);
417 case BUILDING_PAVILION_BLUE:
418 return translation_for(TR_BUILDING_BLUE_PAVILION);
419 case BUILDING_PAVILION_RED:
420 return translation_for(TR_BUILDING_RED_PAVILION);
421 case BUILDING_PAVILION_ORANGE:
422 return translation_for(TR_BUILDING_ORANGE_PAVILION);
423 case BUILDING_PAVILION_YELLOW:
424 return translation_for(TR_BUILDING_YELLOW_PAVILION);
425 case BUILDING_PAVILION_GREEN:
426 return translation_for(TR_BUILDING_GREEN_PAVILION);
427 case BUILDING_SMALL_STATUE_ALT:
428 return translation_for(TR_BUILDING_SMALL_STATUE_ALT);
429 case BUILDING_SMALL_STATUE_ALT_B:
430 return translation_for(TR_BUILDING_SMALL_STATUE_ALT_B);
431 case BUILDING_OBELISK:
432 return translation_for(TR_BUILDING_OBELISK);
433 case BUILDING_LIGHTHOUSE:
434 return translation_for(TR_BUILDING_LIGHTHOUSE);
435 case BUILDING_MENU_GOV_RES:
436 return translation_for(TR_BUILDING_MENU_GOV_RES);
437 case BUILDING_MENU_STATUES:
438 return translation_for(TR_BUILDING_MENU_STATUES);
439 case BUILDING_TAVERN:
440 return translation_for(TR_BUILDING_TAVERN);
441 case BUILDING_GRAND_GARDEN:
442 return translation_for(TR_BUILDING_GRAND_GARDEN);
443 case BUILDING_ARENA:
444 return translation_for(TR_BUILDING_ARENA);
445 case BUILDING_HORSE_STATUE:
446 return translation_for(TR_BUILDING_HORSE_STATUE);
447 case BUILDING_DOLPHIN_FOUNTAIN:
448 return translation_for(TR_BUILDING_DOLPHIN_FOUNTAIN);
449 case BUILDING_HEDGE_DARK:
450 return translation_for(TR_BUILDING_HEDGE_DARK);
451 case BUILDING_HEDGE_LIGHT:
452 return translation_for(TR_BUILDING_HEDGE_LIGHT);
453 case BUILDING_GARDEN_WALL:
454 return translation_for(TR_BUILDING_GARDEN_WALL);
455 case BUILDING_LEGION_STATUE:
456 return translation_for(TR_BUILDING_LEGION_STATUE);
457 case BUILDING_DECORATIVE_COLUMN:
458 return translation_for(TR_BUILDING_DECORATIVE_COLUMN);
459 case BUILDING_COLONNADE:
460 return translation_for(TR_BUILDING_COLONNADE);
461 case BUILDING_GARDEN_PATH:
462 return translation_for(TR_BUILDING_GARDEN_PATH);
463 case BUILDING_LARARIUM:
464 return translation_for(TR_BUILDING_LARARIUM);
465 case BUILDING_NYMPHAEUM:
466 return translation_for(TR_BUILDING_NYMPHAEUM);
467 case BUILDING_WATCHTOWER:
468 return translation_for(TR_BUILDING_WATCHTOWER);
469 case BUILDING_SMALL_MAUSOLEUM:
470 return translation_for(TR_BUILDING_SMALL_MAUSOLEUM);
471 case BUILDING_LARGE_MAUSOLEUM:
472 return translation_for(TR_BUILDING_LARGE_MAUSOLEUM);
473 case BUILDING_CARAVANSERAI:
474 return translation_for(TR_BUILDING_CARAVANSERAI);
475 case BUILDING_ROOFED_GARDEN_WALL:
476 return translation_for(TR_BUILDING_ROOFED_GARDEN_WALL);
477 case BUILDING_GARDEN_WALL_GATE:
478 return translation_for(TR_BUILDING_GARDEN_WALL_GATE);
479 case BUILDING_PALISADE:
480 return translation_for(TR_BUILDING_PALISADE);
481 default:
482 break;
483 }
484 }
485
486 const uint8_t *str = &data.text_data[data.text_entries[group].offset];
487 uint8_t prev = 0;
488 while (index > 0) {
489 if (!*str && (prev >= ' ' || prev == 0)) {
490 --index;
491 }
492 prev = *str;
493 ++str;
494 }
495 while (*str < ' ') { // skip non-printables
496 ++str;
497 }
498 return str;
499 }
500
lang_get_message(int id)501 const lang_message *lang_get_message(int id)
502 {
503 return &data.message_entries[id];
504 }
505