1 #include "core/lang.h"
2
3 #include "core/buffer.h"
4 #include "core/file.h"
5 #include "core/io.h"
6 #include "core/string.h"
7
8 #include <stdlib.h>
9 #include <string.h>
10
11 #define MAX_TEXT_ENTRIES 1000
12 #define MAX_TEXT_DATA 200000
13 #define MIN_TEXT_SIZE (28 + MAX_TEXT_ENTRIES * 8)
14 #define MAX_TEXT_SIZE (MIN_TEXT_SIZE + MAX_TEXT_DATA)
15
16 #define MAX_MESSAGE_ENTRIES 400
17 #define MAX_MESSAGE_DATA 460000
18 #define MIN_MESSAGE_SIZE 32024
19 #define MAX_MESSAGE_SIZE (MIN_MESSAGE_SIZE + MAX_MESSAGE_DATA)
20
21 #define BUFFER_SIZE 400000
22
23 #define FILE_TEXT_ENG "c3.eng"
24 #define FILE_MM_ENG "c3_mm.eng"
25 #define FILE_TEXT_RUS "c3.rus"
26 #define FILE_MM_RUS "c3_mm.rus"
27 #define FILE_EDITOR_TEXT_ENG "c3_map.eng"
28 #define FILE_EDITOR_MM_ENG "c3_map_mm.eng"
29
30 static struct {
31 struct {
32 int32_t offset;
33 int32_t in_use;
34 } text_entries[MAX_TEXT_ENTRIES];
35 uint8_t text_data[MAX_TEXT_DATA];
36
37 lang_message message_entries[MAX_MESSAGE_ENTRIES];
38 uint8_t message_data[MAX_MESSAGE_DATA];
39 } data;
40
file_exists_in_dir(const char * dir,const char * file)41 static int file_exists_in_dir(const char *dir, const char *file)
42 {
43 char path[2 * FILE_NAME_MAX];
44 path[2 * FILE_NAME_MAX - 1] = 0;
45 strncpy(path, dir, 2 * FILE_NAME_MAX - 1);
46 strncat(path, "/", 2 * FILE_NAME_MAX - 1);
47 strncat(path, file, 2 * FILE_NAME_MAX - 1);
48 return file_exists(path, NOT_LOCALIZED);
49 }
50
lang_dir_is_valid(const char * dir)51 int lang_dir_is_valid(const char *dir)
52 {
53 if (file_exists_in_dir(dir, FILE_TEXT_ENG) && file_exists_in_dir(dir, FILE_MM_ENG)) {
54 return 1;
55 }
56 if (file_exists_in_dir(dir, FILE_TEXT_RUS) && file_exists_in_dir(dir, FILE_MM_RUS)) {
57 return 1;
58 }
59 return 0;
60 }
61
parse_text(buffer * buf)62 static void parse_text(buffer *buf)
63 {
64 buffer_skip(buf, 28); // header
65 for (int i = 0; i < MAX_TEXT_ENTRIES; i++) {
66 data.text_entries[i].offset = buffer_read_i32(buf);
67 data.text_entries[i].in_use = buffer_read_i32(buf);
68 }
69 buffer_read_raw(buf, data.text_data, MAX_TEXT_DATA);
70 }
71
load_text(const char * filename,int localizable,uint8_t * buf_data)72 static int load_text(const char *filename, int localizable, uint8_t *buf_data)
73 {
74 buffer buf;
75 int filesize = io_read_file_into_buffer(filename, localizable, buf_data, BUFFER_SIZE);
76 if (filesize < MIN_TEXT_SIZE || filesize > MAX_TEXT_SIZE) {
77 return 0;
78 }
79 buffer_init(&buf, buf_data, filesize);
80 parse_text(&buf);
81 return 1;
82 }
83
get_message_text(int32_t offset)84 static uint8_t *get_message_text(int32_t offset)
85 {
86 if (!offset) {
87 return 0;
88 }
89 return &data.message_data[offset];
90 }
91
parse_message(buffer * buf)92 static void parse_message(buffer *buf)
93 {
94 buffer_skip(buf, 24); // header
95 for (int i = 0; i < MAX_MESSAGE_ENTRIES; i++) {
96 lang_message *m = &data.message_entries[i];
97 m->type = buffer_read_i16(buf);
98 m->message_type = buffer_read_i16(buf);
99 buffer_skip(buf, 2);
100 m->x = buffer_read_i16(buf);
101 m->y = buffer_read_i16(buf);
102 m->width_blocks = buffer_read_i16(buf);
103 m->height_blocks = buffer_read_i16(buf);
104 m->image.id = buffer_read_i16(buf);
105 m->image.x = buffer_read_i16(buf);
106 m->image.y = buffer_read_i16(buf);
107 buffer_skip(buf, 6); // unused image2 id, x, y
108 m->title.x = buffer_read_i16(buf);
109 m->title.y = buffer_read_i16(buf);
110 m->subtitle.x = buffer_read_i16(buf);
111 m->subtitle.y = buffer_read_i16(buf);
112 buffer_skip(buf, 4);
113 m->video.x = buffer_read_i16(buf);
114 m->video.y = buffer_read_i16(buf);
115 buffer_skip(buf, 14);
116 m->urgent = buffer_read_i32(buf);
117
118 m->video.text = get_message_text(buffer_read_i32(buf));
119 buffer_skip(buf, 4);
120 m->title.text = get_message_text(buffer_read_i32(buf));
121 m->subtitle.text = get_message_text(buffer_read_i32(buf));
122 m->content.text = get_message_text(buffer_read_i32(buf));
123 }
124 buffer_read_raw(buf, &data.message_data, MAX_MESSAGE_DATA);
125 }
126
load_message(const char * filename,int localizable,uint8_t * data_buffer)127 static int load_message(const char *filename, int localizable, uint8_t *data_buffer)
128 {
129 buffer buf;
130 int filesize = io_read_file_into_buffer(filename, localizable, data_buffer, BUFFER_SIZE);
131 if (filesize < MIN_MESSAGE_SIZE || filesize > MAX_MESSAGE_SIZE) {
132 return 0;
133 }
134 buffer_init(&buf, data_buffer, filesize);
135 parse_message(&buf);
136 return 1;
137 }
138
load_files(const char * text_filename,const char * message_filename,int localizable)139 static int load_files(const char *text_filename, const char *message_filename, int localizable)
140 {
141 uint8_t *buffer = (uint8_t *) malloc(BUFFER_SIZE);
142 if (!buffer) {
143 return 0;
144 }
145 int success = load_text(text_filename, localizable, buffer) && load_message(message_filename, localizable, buffer);
146 free(buffer);
147 return success;
148 }
149
lang_load(int is_editor)150 int lang_load(int is_editor)
151 {
152 if (is_editor) {
153 return load_files(FILE_EDITOR_TEXT_ENG, FILE_EDITOR_MM_ENG, MAY_BE_LOCALIZED);
154 }
155 // Prefer language files from localized dir, fall back to main dir
156 return
157 load_files(FILE_TEXT_ENG, FILE_MM_ENG, MUST_BE_LOCALIZED) ||
158 load_files(FILE_TEXT_RUS, FILE_MM_RUS, MUST_BE_LOCALIZED) ||
159 load_files(FILE_TEXT_ENG, FILE_MM_ENG, NOT_LOCALIZED) ||
160 load_files(FILE_TEXT_RUS, FILE_MM_RUS, NOT_LOCALIZED);
161 }
162
lang_get_string(int group,int index)163 const uint8_t *lang_get_string(int group, int index)
164 {
165 const uint8_t *str = &data.text_data[data.text_entries[group].offset];
166 uint8_t prev = 0;
167 while (index > 0) {
168 if (!*str && (prev >= ' ' || prev == 0)) {
169 --index;
170 }
171 prev = *str;
172 ++str;
173 }
174 while (*str < ' ') { // skip non-printables
175 ++str;
176 }
177 return str;
178 }
179
lang_get_message(int id)180 const lang_message *lang_get_message(int id)
181 {
182 return &data.message_entries[id];
183 }
184