597 |
mateuszvis |
1 |
/*
|
|
|
2 |
* Copyright (C) 2021-2022 Mateusz Viste
|
|
|
3 |
*
|
|
|
4 |
* usage: tlumacz en fr pl etc
|
|
|
5 |
*
|
601 |
mateuszvis |
6 |
* computes an out.lng file that contains all language ressources.
|
597 |
mateuszvis |
7 |
*
|
|
|
8 |
* DAT format:
|
|
|
9 |
*
|
|
|
10 |
* 4-bytes signature:
|
|
|
11 |
* "SvL\x1b"
|
|
|
12 |
*
|
|
|
13 |
* Then "LANG BLOCKS" follow. Each LANG BLOCK is prefixed with 4 bytes:
|
|
|
14 |
* II LL - II is the LANG identifier ("EN", "PL", etc) and LL is the size
|
|
|
15 |
* of the block (65535 bytes max).
|
|
|
16 |
*
|
|
|
17 |
* Inside a LANG BLOCK is a set of strings:
|
|
|
18 |
*
|
|
|
19 |
* II L S where II is the string's 16-bit identifier, L is its length (1-255)
|
|
|
20 |
* and S is the actual string. All strings are ASCIIZ-formatted (ie.
|
|
|
21 |
* end with a NULL terminator).
|
|
|
22 |
*
|
|
|
23 |
* The list of strings ends with a single 0-long string.
|
|
|
24 |
*/
|
|
|
25 |
|
|
|
26 |
|
|
|
27 |
#include <stdio.h>
|
|
|
28 |
#include <stdlib.h>
|
|
|
29 |
#include <string.h>
|
|
|
30 |
|
|
|
31 |
|
|
|
32 |
|
|
|
33 |
struct bitmap {
|
|
|
34 |
unsigned char bits[8192];
|
|
|
35 |
};
|
|
|
36 |
|
|
|
37 |
static void bitmap_set(struct bitmap *b, unsigned short id) {
|
|
|
38 |
b->bits[id / 8] |= 1 << (id & 7);
|
|
|
39 |
}
|
|
|
40 |
|
|
|
41 |
static int bitmap_get(const struct bitmap *b, unsigned short id) {
|
|
|
42 |
return(b->bits[id / 8] & (1 << (id & 7)));
|
|
|
43 |
}
|
|
|
44 |
|
|
|
45 |
static void bitmap_init(struct bitmap *b) {
|
|
|
46 |
memset(b, 0, sizeof(struct bitmap));
|
|
|
47 |
}
|
|
|
48 |
|
|
|
49 |
|
|
|
50 |
|
|
|
51 |
/* read a single line from fd and fills it into dst, returns line length
|
|
|
52 |
* ending CR/LF is trimmed, as well as any trailing spaces */
|
|
|
53 |
static unsigned short readl(char *dst, size_t dstsz, FILE *fd) {
|
|
|
54 |
unsigned short l, lastnonspace = 0;
|
|
|
55 |
|
|
|
56 |
if (fgets(dst, dstsz, fd) == NULL) return(0xffff); /* EOF */
|
|
|
57 |
/* trim at first CR or LF and return len */
|
|
|
58 |
for (l = 0; (dst[l] != 0) && (dst[l] != '\r') && (dst[l] != '\n'); l++) {
|
|
|
59 |
if (dst[l] != ' ') lastnonspace = l;
|
|
|
60 |
}
|
|
|
61 |
|
|
|
62 |
if (lastnonspace < l) l = lastnonspace + 1; /* rtrim */
|
|
|
63 |
dst[l] = 0;
|
|
|
64 |
|
|
|
65 |
return(l);
|
|
|
66 |
}
|
|
|
67 |
|
|
|
68 |
|
|
|
69 |
/* parse a line in format "1.50:somestring". fills id and returns a pointer to
|
|
|
70 |
* the actual string part on success, or NULL on error */
|
|
|
71 |
static char *parseline(unsigned short *id, char *s) {
|
|
|
72 |
int i;
|
|
|
73 |
int dotpos = 0, colpos = 0, gotdigits = 0;
|
|
|
74 |
|
|
|
75 |
/* I must have a . and a : in the first 9 bytes */
|
|
|
76 |
for (i = 0;; i++) {
|
|
|
77 |
if (s[i] == '.') {
|
|
|
78 |
if ((dotpos != 0) || (gotdigits == 0)) break;
|
|
|
79 |
dotpos = i;
|
|
|
80 |
gotdigits = 0;
|
|
|
81 |
} else if (s[i] == ':') {
|
|
|
82 |
if (gotdigits != 0) colpos = i;
|
|
|
83 |
break;
|
|
|
84 |
} else if ((s[i] < '0') || (s[i] > '9')) {
|
|
|
85 |
break;
|
|
|
86 |
}
|
|
|
87 |
gotdigits++;
|
|
|
88 |
}
|
|
|
89 |
/* did I collect everything? */
|
|
|
90 |
if ((dotpos == 0) || (colpos == 0)) return(NULL);
|
|
|
91 |
if (s[colpos + 1] == 0) return(NULL);
|
|
|
92 |
|
|
|
93 |
*id = atoi(s);
|
|
|
94 |
*id <<= 8;
|
|
|
95 |
*id |= atoi(s + dotpos + 1);
|
|
|
96 |
|
|
|
97 |
/* printf("parseline(): %04X = '%s'\r\n", *id, s + colpos + 1); */
|
|
|
98 |
|
|
|
99 |
return(s + colpos + 1);
|
|
|
100 |
}
|
|
|
101 |
|
|
|
102 |
|
|
|
103 |
/* opens a CATS-style file and compiles it into a ressources lang block */
|
|
|
104 |
static unsigned short gen_langstrings(unsigned char *buff, const char *langid, struct bitmap *b, const struct bitmap *refb, const unsigned char *refblock) {
|
|
|
105 |
unsigned short len = 0, linelen;
|
|
|
106 |
FILE *fd;
|
|
|
107 |
char fname[] = "XX.TXT";
|
|
|
108 |
char linebuf[512];
|
|
|
109 |
char *ptr;
|
|
|
110 |
unsigned short id, linecount;
|
|
|
111 |
|
|
|
112 |
bitmap_init(b);
|
|
|
113 |
|
|
|
114 |
memcpy(fname + strlen(fname) - 6, langid, 2);
|
|
|
115 |
|
|
|
116 |
fd = fopen(fname, "rb");
|
|
|
117 |
if (fd == NULL) {
|
|
|
118 |
printf("ERROR: FAILED TO OPEN '%s'\r\n", fname);
|
|
|
119 |
return(0);
|
|
|
120 |
}
|
|
|
121 |
|
|
|
122 |
for (linecount = 1;; linecount++) {
|
|
|
123 |
|
|
|
124 |
linelen = readl(linebuf, sizeof(linebuf), fd);
|
|
|
125 |
if (linelen == 0xffff) break; /* EOF */
|
|
|
126 |
if ((linelen == 0) || (linebuf[0] == '#')) continue;
|
|
|
127 |
|
|
|
128 |
/* read id and get ptr to actual string ("1.15:string") */
|
|
|
129 |
ptr = parseline(&id, linebuf);
|
|
|
130 |
if (ptr == NULL) {
|
|
|
131 |
printf("ERROR: line #%u of %s is malformed\r\n", linecount, fname);
|
|
|
132 |
len = 0;
|
|
|
133 |
break;
|
|
|
134 |
}
|
|
|
135 |
|
|
|
136 |
/* write string into block (II L S) */
|
|
|
137 |
memcpy(buff + len, &id, 2);
|
|
|
138 |
len += 2;
|
|
|
139 |
buff[len++] = strlen(ptr) + 1;
|
|
|
140 |
memcpy(buff + len, ptr, strlen(ptr) + 1);
|
|
|
141 |
len += strlen(ptr) + 1;
|
|
|
142 |
|
|
|
143 |
/* if reference bitmap provided: check that the id is valid */
|
|
|
144 |
if ((refb != NULL) && (bitmap_get(refb, id) == 0)) {
|
|
|
145 |
printf("WARNING: %s[#%u] has an invalid id (%u.%u not present in ref lang)\r\n", fname, linecount, id >> 8, id & 0xff);
|
|
|
146 |
}
|
|
|
147 |
|
|
|
148 |
/* make sure this id is not already present */
|
|
|
149 |
if (bitmap_get(b, id) == 0) {
|
|
|
150 |
/* set bit in bitmap to remember I have this string */
|
|
|
151 |
bitmap_set(b, id);
|
|
|
152 |
} else {
|
|
|
153 |
printf("WARNING: %s[#%u] has a duplicated id (%u.%u)\r\n", fname, linecount, id >> 8, id & 0xff);
|
|
|
154 |
}
|
|
|
155 |
}
|
|
|
156 |
|
|
|
157 |
fclose(fd);
|
|
|
158 |
|
|
|
159 |
/* if refblock provided, pull missing strings from it */
|
|
|
160 |
if (refblock != NULL) {
|
|
|
161 |
for (;;) {
|
|
|
162 |
id = *((unsigned short *)refblock);
|
|
|
163 |
if ((id == 0) && (refblock[2] == 0)) break;
|
|
|
164 |
if (bitmap_get(b, id) == 0) {
|
|
|
165 |
printf("WARNING: %s is missing string %u.%u (pulled from ref lang)\r\n", fname, id >> 8, id & 0xff);
|
|
|
166 |
/* copy missing string from refblock */
|
|
|
167 |
memcpy(buff + len, refblock, refblock[2] + 3);
|
|
|
168 |
len += refblock[2] + 3;
|
|
|
169 |
}
|
|
|
170 |
refblock += refblock[2] + 3;
|
|
|
171 |
}
|
|
|
172 |
}
|
|
|
173 |
|
|
|
174 |
/* write the block terminator (0-long string) */
|
|
|
175 |
buff[len++] = 0; /* id */
|
|
|
176 |
buff[len++] = 0; /* id */
|
|
|
177 |
buff[len++] = 0; /* len */
|
|
|
178 |
|
|
|
179 |
return(len);
|
|
|
180 |
}
|
|
|
181 |
|
|
|
182 |
|
599 |
mateuszvis |
183 |
#define MEMBLOCKSZ 65000
|
597 |
mateuszvis |
184 |
|
|
|
185 |
int main(int argc, char **argv) {
|
|
|
186 |
FILE *fd;
|
|
|
187 |
int ecode = 0;
|
|
|
188 |
char *buff, *refblock;
|
|
|
189 |
static struct bitmap bufbitmap;
|
|
|
190 |
static struct bitmap refbitmap;
|
|
|
191 |
unsigned short i;
|
|
|
192 |
|
|
|
193 |
if (argc < 2) {
|
|
|
194 |
puts("usage: tlumacz en fr pl etc");
|
|
|
195 |
return(1);
|
|
|
196 |
}
|
|
|
197 |
|
|
|
198 |
buff = malloc(MEMBLOCKSZ);
|
|
|
199 |
refblock = malloc(MEMBLOCKSZ);
|
|
|
200 |
if ((buff == NULL) || (refblock == NULL)) {
|
|
|
201 |
puts("out of memory");
|
|
|
202 |
return(1);
|
|
|
203 |
}
|
|
|
204 |
|
601 |
mateuszvis |
205 |
fd = fopen("out.lng", "wb");
|
597 |
mateuszvis |
206 |
if (fd == NULL) {
|
|
|
207 |
puts("ERR: failed to open or create SVARCOM.LNG");
|
|
|
208 |
return(1);
|
|
|
209 |
}
|
|
|
210 |
|
|
|
211 |
/* write sig */
|
|
|
212 |
fwrite("SvL\x1b", 1, 4, fd);
|
|
|
213 |
|
|
|
214 |
/* write lang blocks */
|
|
|
215 |
for (i = 1; i < argc; i++) {
|
|
|
216 |
unsigned short sz;
|
|
|
217 |
char id[3];
|
|
|
218 |
|
|
|
219 |
if (strlen(argv[i]) != 2) {
|
|
|
220 |
printf("INVALID LANG SPECIFIED: %s\r\n", argv[i]);
|
|
|
221 |
ecode = 1;
|
|
|
222 |
break;
|
|
|
223 |
}
|
|
|
224 |
|
|
|
225 |
id[0] = argv[i][0];
|
|
|
226 |
id[1] = argv[i][1];
|
|
|
227 |
id[2] = 0;
|
|
|
228 |
if (id[0] >= 'a') id[0] -= 'a' - 'A';
|
|
|
229 |
if (id[1] >= 'a') id[1] -= 'a' - 'A';
|
|
|
230 |
|
|
|
231 |
sz = gen_langstrings(buff, id, &bufbitmap, (i != 1)?&refbitmap:NULL, (i != 1)?refblock:NULL);
|
|
|
232 |
if (sz == 0) {
|
|
|
233 |
printf("ERROR COMPUTING LANG '%s'\r\n", id);
|
|
|
234 |
ecode = 1;
|
|
|
235 |
break;
|
|
|
236 |
} else {
|
|
|
237 |
printf("computed %s lang block of %u bytes\r\n", id, sz);
|
|
|
238 |
}
|
|
|
239 |
/* write lang ID to file, followed by block size and then the actual block */
|
|
|
240 |
if ((fwrite(id, 1, 2, fd) != 2) ||
|
|
|
241 |
(fwrite(&sz, 1, 2, fd) != 2) ||
|
|
|
242 |
(fwrite(buff, 1, sz, fd) != sz)) {
|
|
|
243 |
printf("ERROR WRITING TO OUTPUT FILE\r\n");
|
|
|
244 |
ecode = 1;
|
|
|
245 |
break;
|
|
|
246 |
}
|
|
|
247 |
/* compute the default block for reference language */
|
|
|
248 |
if (i == 1) {
|
|
|
249 |
unsigned short x;
|
|
|
250 |
FILE *fd2;
|
|
|
251 |
fd2 = fopen("DEFLANG.C", "wb");
|
|
|
252 |
if (fd2 == NULL) {
|
|
|
253 |
puts("ERROR: FAILED TO OPEN OR CREATE DEFLANG.C");
|
|
|
254 |
break;
|
|
|
255 |
}
|
|
|
256 |
fprintf(fd2, "/* THIS FILE HAS BEEN AUTOGENERATE BY TLUMACZ (PART OF THE SVARLANG LIBRARY) */\r\n");
|
599 |
mateuszvis |
257 |
fprintf(fd2, "const unsigned short svarlang_memsz = %uu;\r\n", sz * 2);
|
|
|
258 |
fprintf(fd2, "char svarlang_mem[%u] = {\r\n", sz * 2);
|
597 |
mateuszvis |
259 |
for (x = 0; x < sz; x++) {
|
|
|
260 |
fprintf(fd2, "%u", buff[x]);
|
|
|
261 |
if (x + 1 < sz) fprintf(fd2, ",");
|
|
|
262 |
if ((x & 15) == 15) fprintf(fd2, "\r\n");
|
|
|
263 |
}
|
|
|
264 |
fprintf(fd2, "};\r\n");
|
|
|
265 |
fclose(fd2);
|
|
|
266 |
/* remember reference data for other languages */
|
|
|
267 |
memcpy(refblock, buff, MEMBLOCKSZ);
|
|
|
268 |
memcpy(&refbitmap, &bufbitmap, sizeof(struct bitmap));
|
|
|
269 |
}
|
|
|
270 |
}
|
|
|
271 |
|
|
|
272 |
fclose(fd);
|
|
|
273 |
|
|
|
274 |
return(ecode);
|
|
|
275 |
}
|