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