Subversion Repositories SvarDOS

Rev

Rev 1295 | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 1295 Rev 1296
1
/*
1
/*
2
 * Copyright (C) 2021-2023 Mateusz Viste
2
 * Copyright (C) 2021-2023 Mateusz Viste
3
 *
3
 *
4
 * Dictionary-based lookups contributed by Bernd Boeckmann, 2023
4
 * Dictionary-based lookups contributed by Bernd Boeckmann, 2023
5
 *
5
 *
6
 * usage: tlumacz en fr pl etc
6
 * usage: tlumacz en fr pl etc
7
 *
7
 *
8
 * computes an out.lng file that contains all language resources.
8
 * computes an out.lng file that contains all language resources.
9
 *
9
 *
10
 */
10
 */
11
 
11
 
12
 
12
 
13
#include <stdio.h>
13
#include <stdio.h>
14
#include <stdlib.h>
14
#include <stdlib.h>
15
#include <string.h>
15
#include <string.h>
16
#include <ctype.h>
16
#include <ctype.h>
17
 
17
 
18
#include "svarlang.h"
18
#include "svarlang.h"
19
 
19
 
20
#define STRINGS_CAP 65000   /* string storage size in characters */
20
#define STRINGS_CAP 65000   /* string storage size in characters */
21
#define DICT_CAP    10000   /* dictionary size in elements */
21
#define DICT_CAP    10000   /* dictionary size in elements */
22
 
22
 
23
/* read a single line from fd and fills it into dst, returns line length
23
/* read a single line from fd and fills it into dst, returns line length
24
 * ending CR/LF is trimmed, as well as any trailing spaces */
24
 * ending CR/LF is trimmed, as well as any trailing spaces */
25
static unsigned short readl(char *dst, size_t dstsz, FILE *fd) {
25
static unsigned short readl(char *dst, size_t dstsz, FILE *fd) {
26
  unsigned short l, lastnonspace = 0;
26
  unsigned short l, lastnonspace = 0;
27
 
27
 
28
  if (fgets(dst, (int)dstsz, fd) == NULL) return(0xffff); /* EOF */
28
  if (fgets(dst, (int)dstsz, fd) == NULL) return(0xffff); /* EOF */
29
  /* trim at first CR or LF and return len */
29
  /* trim at first CR or LF and return len */
30
  for (l = 0; (dst[l] != 0) && (dst[l] != '\r') && (dst[l] != '\n'); l++) {
30
  for (l = 0; (dst[l] != 0) && (dst[l] != '\r') && (dst[l] != '\n'); l++) {
31
    if (dst[l] != ' ') lastnonspace = l;
31
    if (dst[l] != ' ') lastnonspace = l;
32
  }
32
  }
33
 
33
 
34
  if (lastnonspace < l) l = lastnonspace + 1; /* rtrim */
34
  if (lastnonspace < l) l = lastnonspace + 1; /* rtrim */
35
  dst[l] = 0;
35
  dst[l] = 0;
36
 
36
 
37
  return(l);
37
  return(l);
38
}
38
}
39
 
39
 
40
 
40
 
41
/* parse a line in format "[?]1.50:somestring". fills id and returns a pointer to
41
/* parse a line in format "[?]1.50:somestring". fills id and returns a pointer to
42
 * the actual string part on success, or NULL on error */
42
 * the actual string part on success, or NULL on error */
43
static const char *parseline(unsigned short *id, const char *s) {
43
static const char *parseline(unsigned short *id, const char *s) {
44
  int i;
44
  int i;
45
  int dotpos = 0, colpos = 0, gotdigits = 0;
45
  int dotpos = 0, colpos = 0, gotdigits = 0;
46
 
46
 
47
  /* strings prefixed by '?' are flagged as "dirty": ignore this flag here */
47
  /* strings prefixed by '?' are flagged as "dirty": ignore this flag here */
48
  if (*s == '?') s++;
48
  if (*s == '?') s++;
49
 
49
 
50
  /* I must have a . and a : in the first 9 bytes */
50
  /* I must have a . and a : in the first 9 bytes */
51
  for (i = 0;; i++) {
51
  for (i = 0;; i++) {
52
    if (s[i] == '.') {
52
    if (s[i] == '.') {
53
      if ((dotpos != 0) || (gotdigits == 0)) break;
53
      if ((dotpos != 0) || (gotdigits == 0)) break;
54
      dotpos = i;
54
      dotpos = i;
55
      gotdigits = 0;
55
      gotdigits = 0;
56
    } else if (s[i] == ':') {
56
    } else if (s[i] == ':') {
57
      if (gotdigits != 0) colpos = i;
57
      if (gotdigits != 0) colpos = i;
58
      break;
58
      break;
59
    } else if ((s[i] < '0') || (s[i] > '9')) {
59
    } else if ((s[i] < '0') || (s[i] > '9')) {
60
      break;
60
      break;
61
    }
61
    }
62
    gotdigits++;
62
    gotdigits++;
63
  }
63
  }
64
  /* did I collect everything? */
64
  /* did I collect everything? */
65
  if ((dotpos == 0) || (colpos == 0)) return(NULL);
65
  if ((dotpos == 0) || (colpos == 0)) return(NULL);
66
 
66
 
67
  *id = atoi(s);
67
  *id = atoi(s);
68
  *id <<= 8;
68
  *id <<= 8;
69
  *id |= atoi(s + dotpos + 1);
69
  *id |= atoi(s + dotpos + 1);
70
 
70
 
71
  /* printf("parseline(): %04X = '%s'\r\n", *id, s + colpos + 1); */
71
  /* printf("parseline(): %04X = '%s'\r\n", *id, s + colpos + 1); */
72
 
72
 
73
  return(s + colpos + 1);
73
  return(s + colpos + 1);
74
}
74
}
75
 
75
 
76
 
76
 
77
/* converts escape sequences like "\n" or "\t" into actual bytes, returns
77
/* converts escape sequences like "\n" or "\t" into actual bytes, returns
78
 * the new length of the string. */
78
 * the new length of the string. */
79
static unsigned short unesc_string(char *linebuff) {
79
static unsigned short unesc_string(char *linebuff) {
80
  unsigned short i;
80
  unsigned short i;
81
  for (i = 0; linebuff[i] != 0; i++) {
81
  for (i = 0; linebuff[i] != 0; i++) {
82
    if (linebuff[i] != '\\') continue;
82
    if (linebuff[i] != '\\') continue;
83
    memmove(linebuff + i, linebuff + i + 1, strlen(linebuff + i));
83
    memmove(linebuff + i, linebuff + i + 1, strlen(linebuff + i));
84
    if (linebuff[i] == 0) break;
84
    if (linebuff[i] == 0) break;
85
    switch (linebuff[i]) {
85
    switch (linebuff[i]) {
86
      case 'e':
86
      case 'e':
87
        linebuff[i] = 0x1B; /* ESC code, using hex because '\e' is not ANSI C */
87
        linebuff[i] = 0x1B; /* ESC code, using hex because '\e' is not ANSI C */
88
        break;
88
        break;
89
      case 'n':
89
      case 'n':
90
        linebuff[i] = '\n';
90
        linebuff[i] = '\n';
91
        break;
91
        break;
92
      case 'r':
92
      case 'r':
93
        linebuff[i] = '\r';
93
        linebuff[i] = '\r';
94
        break;
94
        break;
95
      case 't':
95
      case 't':
96
        linebuff[i] = '\t';
96
        linebuff[i] = '\t';
97
        break;
97
        break;
98
    }
98
    }
99
  }
99
  }
100
  return(i);
100
  return(i);
101
}
101
}
102
 
102
 
103
#pragma pack(1)
103
#pragma pack(1)
104
typedef struct dict_entry {
104
struct dict_entry {
105
  unsigned short id;
105
  unsigned short id;
106
  unsigned short offset;
106
  unsigned short offset;
107
} dict_entry_t;
107
};
108
#pragma pack()
108
#pragma pack()
109
 
109
 
110
typedef struct svl_lang {
110
struct svl_lang {
111
  char id[2];
111
  char id[2];
112
  unsigned short num_strings;
112
  unsigned short num_strings;
113
 
113
 
114
  dict_entry_t *dict;
114
  struct dict_entry *dict;
115
  size_t dict_cap;
115
  size_t dict_cap;
116
 
116
 
117
  char *strings;
117
  char *strings;
118
  char *strings_end;
118
  char *strings_end;
119
  size_t strings_cap;
119
  size_t strings_cap;
120
 
120
 
121
} svl_lang_t;
121
};
122
 
122
 
123
 
123
 
124
static svl_lang_t *svl_lang_new(const char langid[2], size_t dict_cap, size_t strings_cap) {
124
static struct svl_lang *svl_lang_new(const char langid[2], size_t dict_cap, size_t strings_cap) {
125
  svl_lang_t *l;
125
  struct svl_lang *l;
126
 
126
 
127
  l = malloc(sizeof(svl_lang_t));
127
  l = malloc(sizeof(struct svl_lang));
128
  if (!l) return(NULL);
128
  if (!l) return(NULL);
129
 
129
 
130
  l->id[0] = (char)toupper(langid[0]);
130
  l->id[0] = (char)toupper(langid[0]);
131
  l->id[1] = (char)toupper(langid[1]);
131
  l->id[1] = (char)toupper(langid[1]);
132
 
132
 
133
  l->dict = malloc(dict_cap * sizeof(dict_entry_t));
133
  l->dict = malloc(dict_cap * sizeof(struct dict_entry));
134
  if (!l->dict) return(NULL);
134
  if (!l->dict) return(NULL);
135
 
135
 
136
  l->dict_cap = dict_cap;
136
  l->dict_cap = dict_cap;
137
 
137
 
138
  l->num_strings = 0;
138
  l->num_strings = 0;
139
  l->strings = l->strings_end = malloc(strings_cap);
139
  l->strings = l->strings_end = malloc(strings_cap);
140
  if (!l->strings) {
140
  if (!l->strings) {
141
    free(l->dict);
141
    free(l->dict);
142
    return(NULL);
142
    return(NULL);
143
  }
143
  }
144
  l->strings_cap = strings_cap;
144
  l->strings_cap = strings_cap;
145
 
145
 
146
  return(l);
146
  return(l);
147
}
147
}
148
 
148
 
149
 
149
 
150
/* compacts the dict and string buffer */
150
/* compacts the dict and string buffer */
151
static void svl_compact_lang(svl_lang_t *l) {
151
static void svl_compact_lang(struct svl_lang *l) {
152
  size_t bytes;
152
  size_t bytes;
153
  bytes = l->strings_end - l->strings;
153
  bytes = l->strings_end - l->strings;
154
  if (bytes < l->strings_cap) {
154
  if (bytes < l->strings_cap) {
155
    l->strings = l->strings_end = realloc(l->strings, bytes);
155
    l->strings = l->strings_end = realloc(l->strings, bytes);
156
    l->strings_end += bytes;
156
    l->strings_end += bytes;
157
    l->strings_cap = bytes;
157
    l->strings_cap = bytes;
158
  }
158
  }
159
  l->dict_cap = l->num_strings;
159
  l->dict_cap = l->num_strings;
160
  l->dict = realloc(l->dict, l->dict_cap * sizeof(dict_entry_t));
160
  l->dict = realloc(l->dict, l->dict_cap * sizeof(struct dict_entry));
161
}
161
}
162
 
162
 
163
 
163
 
164
static void svl_lang_free(svl_lang_t *l) {
164
static void svl_lang_free(struct svl_lang *l) {
165
  l->num_strings = 0;
165
  l->num_strings = 0;
166
  if (l->dict) {
166
  if (l->dict) {
167
    free(l->dict);
167
    free(l->dict);
168
    l->dict = NULL;
168
    l->dict = NULL;
169
  }
169
  }
170
  if (l->strings) {
170
  if (l->strings) {
171
    free(l->strings);
171
    free(l->strings);
172
    l->strings = l->strings_end = NULL;
172
    l->strings = l->strings_end = NULL;
173
  }
173
  }
174
  l->dict_cap = 0;
174
  l->dict_cap = 0;
175
  l->strings_cap = 0;
175
  l->strings_cap = 0;
176
}
176
}
177
 
177
 
178
 
178
 
179
static size_t svl_strings_bytes(const svl_lang_t *l) {
179
static size_t svl_strings_bytes(const struct svl_lang *l) {
180
  return(l->strings_end - l->strings);
180
  return(l->strings_end - l->strings);
181
}
181
}
182
 
182
 
183
 
183
 
184
static size_t svl_dict_bytes(const svl_lang_t *l) {
184
static size_t svl_dict_bytes(const struct svl_lang *l) {
185
  return(l->num_strings * sizeof(dict_entry_t));
185
  return(l->num_strings * sizeof(struct dict_entry));
186
}
186
}
187
 
187
 
188
 
188
 
189
static int svl_add_str(svl_lang_t *l, unsigned short id, const char *s) {
189
static int svl_add_str(struct svl_lang *l, unsigned short id, const char *s) {
190
  size_t len = strlen(s) + 1;
190
  size_t len = strlen(s) + 1;
191
  size_t cursor;
191
  size_t cursor;
192
 
192
 
193
  if ((l->strings_cap < svl_strings_bytes(l) + len) || (l->dict_cap < (l->num_strings + 1) * sizeof(dict_entry_t))) {
193
  if ((l->strings_cap < svl_strings_bytes(l) + len) || (l->dict_cap < (l->num_strings + 1) * sizeof(struct dict_entry))) {
194
    return(0);
194
    return(0);
195
  }
195
  }
196
 
196
 
197
  /* find dictionary insert position, search backwards in assumption
197
  /* find dictionary insert position, search backwards in assumption
198
     that in translation files, strings are generally ordered ascending */
198
     that in translation files, strings are generally ordered ascending */
199
  for (cursor = l->num_strings; cursor > 0 && l->dict[cursor-1].id > id; cursor--);
199
  for (cursor = l->num_strings; cursor > 0 && l->dict[cursor-1].id > id; cursor--);
200
 
200
 
201
  memmove(&(l->dict[cursor+1]), &(l->dict[cursor]), sizeof(dict_entry_t)*(l->num_strings - cursor));
201
  memmove(&(l->dict[cursor+1]), &(l->dict[cursor]), sizeof(struct dict_entry) * (l->num_strings - cursor));
202
  l->dict[cursor].id = id;
202
  l->dict[cursor].id = id;
203
  l->dict[cursor].offset = l->strings_end - l->strings;
203
  l->dict[cursor].offset = l->strings_end - l->strings;
204
 
204
 
205
  memcpy(l->strings_end, s, len);
205
  memcpy(l->strings_end, s, len);
206
  l->strings_end += len;
206
  l->strings_end += len;
207
  l->num_strings++;
207
  l->num_strings++;
208
 
208
 
209
  return(1);
209
  return(1);
210
}
210
}
211
 
211
 
212
 
212
 
213
static int svl_find(const svl_lang_t *l, unsigned short id) {
213
static int svl_find(const struct svl_lang *l, unsigned short id) {
214
  size_t left = 0, right = l->num_strings - 1, x;
214
  size_t left = 0, right = l->num_strings - 1, x;
215
  unsigned short v;
215
  unsigned short v;
216
 
216
 
217
  if (l->num_strings == 0) return(0);
217
  if (l->num_strings == 0) return(0);
218
 
218
 
219
  while (left <= right ) {
219
  while (left <= right ) {
220
    x = left + ( (right - left ) >> 2 );
220
    x = left + ( (right - left ) >> 2 );
221
    v = l->dict[x].id;
221
    v = l->dict[x].id;
222
    if ( id == v ) {
222
    if ( id == v ) return(1); /* found! */
223
      return(1);
223
 
224
    } else if (id > v) {
224
    if (id > v) {
225
      left = x + 1;
225
      left = x + 1;
226
    } else {
226
    } else {
227
      right = x - 1;
227
      right = x - 1;
228
    }
228
    }
229
  }
229
  }
230
  return(0);
230
  return(0);
231
}
231
}
232
 
232
 
233
 
233
 
234
/* opens a CATS-style file and compiles it into a ressources lang block
234
/* opens a CATS-style file and compiles it into a ressources lang block
235
 * returns 0 on error, or the size of the generated data block otherwise */
235
 * returns 0 on error, or the size of the generated data block otherwise */
236
static unsigned short svl_lang_from_cats_file(svl_lang_t *l, svl_lang_t *refl) {
236
static unsigned short svl_lang_from_cats_file(struct svl_lang *l, struct svl_lang *refl) {
237
  unsigned short linelen;
237
  unsigned short linelen;
238
  FILE *fd;
238
  FILE *fd;
239
  char fname[] = "xx.txt";
239
  char fname[] = "xx.txt";
240
  static char linebuf[8192];
240
  static char linebuf[8192];
241
  const char *ptr;
241
  const char *ptr;
242
  unsigned short id, maxid=0, maxid_line, linecount;
242
  unsigned short id, maxid=0, maxid_line, linecount;
243
  int i;
243
  int i;
244
 
244
 
245
  fname[strlen(fname) - 6] = (char)tolower( l->id[0] );
245
  fname[strlen(fname) - 6] = (char)tolower( l->id[0] );
246
  fname[strlen(fname) - 5] = (char)tolower( l->id[1] );
246
  fname[strlen(fname) - 5] = (char)tolower( l->id[1] );
247
 
247
 
248
  fd = fopen(fname, "rb");
248
  fd = fopen(fname, "rb");
249
  if (fd == NULL) {
249
  if (fd == NULL) {
250
    printf("ERROR: FAILED TO OPEN '%s'\r\n", fname);
250
    printf("ERROR: FAILED TO OPEN '%s'\r\n", fname);
251
    return(0);
251
    return(0);
252
  }
252
  }
253
 
253
 
254
  for (linecount = 1;; linecount++) {
254
  for (linecount = 1;; linecount++) {
255
    linelen = readl(linebuf, sizeof(linebuf), fd);
255
    linelen = readl(linebuf, sizeof(linebuf), fd);
256
    if (linelen == 0xffff) break; /* EOF */
256
    if (linelen == 0xffff) break; /* EOF */
257
    if ((linelen == 0) || (linebuf[0] == '#')) continue;
257
    if ((linelen == 0) || (linebuf[0] == '#')) continue;
258
 
258
 
259
    /* convert escaped chars to actual bytes (\n -> newline, etc) */
259
    /* convert escaped chars to actual bytes (\n -> newline, etc) */
260
    linelen = unesc_string(linebuf);
260
    linelen = unesc_string(linebuf);
261
 
261
 
262
    /* read id and get ptr to actual string ("1.15:string") */
262
    /* read id and get ptr to actual string ("1.15:string") */
263
    ptr = parseline(&id, linebuf);
263
    ptr = parseline(&id, linebuf);
264
 
264
 
265
    /* handle malformed lines */
265
    /* handle malformed lines */
266
    if (ptr == NULL) {
266
    if (ptr == NULL) {
267
      printf("WARNING: %s[#%u] is malformed (linelen = %u):\r\n", fname, linecount, linelen);
267
      printf("WARNING: %s[#%u] is malformed (linelen = %u):\r\n", fname, linecount, linelen);
268
      puts(linebuf);
268
      puts(linebuf);
269
      continue;
269
      continue;
270
    }
270
    }
271
 
271
 
272
    /* ignore empty strings (but emit a warning) */
272
    /* ignore empty strings (but emit a warning) */
273
    if (ptr[0] == 0) {
273
    if (ptr[0] == 0) {
274
      printf("WARNING: %s[#%u] ignoring empty string %u.%u\r\n", fname, linecount, id >> 8, id & 0xff);
274
      printf("WARNING: %s[#%u] ignoring empty string %u.%u\r\n", fname, linecount, id >> 8, id & 0xff);
275
      continue;
275
      continue;
276
    }
276
    }
277
 
277
 
278
    /* warn about dirty lines */
278
    /* warn about dirty lines */
279
    if (linebuf[0] == '?') {
279
    if (linebuf[0] == '?') {
280
      printf("WARNING: %s[#%u] string id %u.%u is flagged as 'dirty'\r\n", fname, linecount, id >> 8, id & 0xff);
280
      printf("WARNING: %s[#%u] string id %u.%u is flagged as 'dirty'\r\n", fname, linecount, id >> 8, id & 0xff);
281
    }
281
    }
282
 
282
 
283
    /* add the string contained in current line, if conditions are met */
283
    /* add the string contained in current line, if conditions are met */
284
    if (!svl_find(l, id)) {
284
    if (!svl_find(l, id)) {
285
      if ((refl == NULL) || (svl_find(refl, id))) {
285
      if ((refl == NULL) || (svl_find(refl, id))) {
286
        if (!svl_add_str(l, id, ptr)) {
286
        if (!svl_add_str(l, id, ptr)) {
287
          printf("ERROR: %s[#%u] output size limit exceeded\r\n", fname, linecount);
287
          printf("ERROR: %s[#%u] output size limit exceeded\r\n", fname, linecount);
288
          fclose(fd);
288
          fclose(fd);
289
          return(0);
289
          return(0);
290
        }
290
        }
291
        if (id >= maxid) {
291
        if (id >= maxid) {
292
          maxid = id;
292
          maxid = id;
293
          maxid_line = linecount;
293
          maxid_line = linecount;
294
        } else {
294
        } else {
295
          printf("WARNING:%s[#%u] file unsorted - line %u has higher id %u.%u\r\n", fname, linecount, maxid_line, maxid >> 8, maxid & 0xff);
295
          printf("WARNING:%s[#%u] file unsorted - line %u has higher id %u.%u\r\n", fname, linecount, maxid_line, maxid >> 8, maxid & 0xff);
296
        }
296
        }
297
      } else {
297
      } else {
298
        printf("WARNING: %s[#%u] has an invalid id (%u.%u not present in ref lang)\r\n", fname, linecount, id >> 8, id & 0xff);
298
        printf("WARNING: %s[#%u] has an invalid id (%u.%u not present in ref lang)\r\n", fname, linecount, id >> 8, id & 0xff);
299
      }
299
      }
300
    } else {
300
    } else {
301
      printf("WARNING: %s[#%u] has a duplicated id (%u.%u)\r\n", fname, linecount, id >> 8, id & 0xff);
301
      printf("WARNING: %s[#%u] has a duplicated id (%u.%u)\r\n", fname, linecount, id >> 8, id & 0xff);
302
    }
302
    }
303
  }
303
  }
304
 
304
 
305
  fclose(fd);
305
  fclose(fd);
306
 
306
 
307
  /* if reflang provided, pull missing strings from it */
307
  /* if reflang provided, pull missing strings from it */
308
  if (refl != NULL) {
308
  if (refl != NULL) {
309
    for (i = 0; i < refl->num_strings; i++) {
309
    for (i = 0; i < refl->num_strings; i++) {
310
      id = refl->dict[i].id;
310
      id = refl->dict[i].id;
311
      if (!svl_find(l, id)) {
311
      if (!svl_find(l, id)) {
312
        printf("WARNING: %s is missing string %u.%u (pulled from ref lang)\r\n", fname, id >> 8, id & 0xff);
312
        printf("WARNING: %s is missing string %u.%u (pulled from ref lang)\r\n", fname, id >> 8, id & 0xff);
313
        if (!svl_add_str(l, id, refl->strings + refl->dict[i].offset)) {
313
        if (!svl_add_str(l, id, refl->strings + refl->dict[i].offset)) {
314
          printf("ERROR: %s[#%u] output size limit exceeded\r\n", fname, linecount);
314
          printf("ERROR: %s[#%u] output size limit exceeded\r\n", fname, linecount);
315
          return(0);
315
          return(0);
316
        }
316
        }
317
      }
317
      }
318
    }
318
    }
319
  }
319
  }
320
 
320
 
321
  return(svl_strings_bytes(l));
321
  return(svl_strings_bytes(l));
322
}
322
}
323
 
323
 
324
 
324
 
325
static int svl_write_header(unsigned short num_strings, FILE *fd) {
325
static int svl_write_header(unsigned short num_strings, FILE *fd) {
326
  return((fwrite("SvL\x1a", 1, 4, fd) == 4) && (fwrite(&num_strings, 1, 2, fd) == 2));
326
  return((fwrite("SvL\x1a", 1, 4, fd) == 4) && (fwrite(&num_strings, 1, 2, fd) == 2));
327
}
327
}
328
 
328
 
329
 
329
 
330
static int svl_write_lang(const svl_lang_t *l, FILE *fd) {
330
static int svl_write_lang(const struct svl_lang *l, FILE *fd) {
331
  unsigned short strings_bytes = svl_strings_bytes(l);
331
  unsigned short strings_bytes = svl_strings_bytes(l);
332
 
332
 
333
  return((fwrite(&l->id, 1, 2, fd) == 2) &&
333
  return((fwrite(&l->id, 1, 2, fd) == 2) &&
334
         (fwrite(&strings_bytes, 1, 2, fd) == 2) &&
334
         (fwrite(&strings_bytes, 1, 2, fd) == 2) &&
335
         (fwrite(l->dict, 1, svl_dict_bytes(l), fd) == svl_dict_bytes(l)) &&
335
         (fwrite(l->dict, 1, svl_dict_bytes(l), fd) == svl_dict_bytes(l)) &&
336
         (fwrite(l->strings, 1, svl_strings_bytes(l), fd) == svl_strings_bytes(l)));
336
         (fwrite(l->strings, 1, svl_strings_bytes(l), fd) == svl_strings_bytes(l)));
337
}
337
}
338
 
338
 
339
 
339
 
340
static int svl_write_c_source(const svl_lang_t *l, const char *fn, unsigned short biggest_langsz) {
340
static int svl_write_c_source(const struct svl_lang *l, const char *fn, unsigned short biggest_langsz) {
341
  FILE *fd;
341
  FILE *fd;
342
  int i;
342
  int i;
343
  unsigned short strings_bytes = svl_strings_bytes(l);
343
  unsigned short strings_bytes = svl_strings_bytes(l);
344
  unsigned short nextnlat = 0;
344
  unsigned short nextnlat = 0;
345
  unsigned short allocsz;
345
  unsigned short allocsz;
346
 
346
 
347
  fd = fopen(fn, "wb");
347
  fd = fopen(fn, "wb");
348
  if (fd == NULL) {
348
  if (fd == NULL) {
349
    puts("ERROR: FAILED TO OPEN OR CREATE DEFLANG.C");
349
    puts("ERROR: FAILED TO OPEN OR CREATE DEFLANG.C");
350
    return(0);
350
    return(0);
351
  }
351
  }
352
 
352
 
353
  allocsz = biggest_langsz + (biggest_langsz / 20);
353
  allocsz = biggest_langsz + (biggest_langsz / 20);
354
  printf("biggest lang block is %u bytes -> allocating a %u bytes buffer (5%% safety margin)\n", biggest_langsz, allocsz);
354
  printf("biggest lang block is %u bytes -> allocating a %u bytes buffer (5%% safety margin)\n", biggest_langsz, allocsz);
355
  fprintf(fd, "/* THIS FILE HAS BEEN GENERATED BY TLUMACZ (PART OF THE SVARLANG LIBRARY) */\r\n");
355
  fprintf(fd, "/* THIS FILE HAS BEEN GENERATED BY TLUMACZ (PART OF THE SVARLANG LIBRARY) */\r\n");
356
  fprintf(fd, "const unsigned short svarlang_memsz = %uu;\r\n", allocsz);
356
  fprintf(fd, "const unsigned short svarlang_memsz = %uu;\r\n", allocsz);
357
  fprintf(fd, "const unsigned short svarlang_string_count = %uu;\r\n\r\n", l->num_strings);
357
  fprintf(fd, "const unsigned short svarlang_string_count = %uu;\r\n\r\n", l->num_strings);
358
  fprintf(fd, "char svarlang_mem[%u] = {\r\n", allocsz);
358
  fprintf(fd, "char svarlang_mem[%u] = {\r\n", allocsz);
359
 
359
 
360
  for (i = 0; i < strings_bytes; i++) {
360
  for (i = 0; i < strings_bytes; i++) {
361
    if (!fprintf(fd, "0x%02x", l->strings[i])) {
361
    if (!fprintf(fd, "0x%02x", l->strings[i])) {
362
      fclose(fd);
362
      fclose(fd);
363
      return(0);
363
      return(0);
364
    }
364
    }
365
 
365
 
366
    if (i + 1 < strings_bytes) fprintf(fd, ",");
366
    if (i + 1 < strings_bytes) fprintf(fd, ",");
367
    nextnlat++;
367
    nextnlat++;
368
    if (l->strings[i] == '\0' || nextnlat == 16) {
368
    if (l->strings[i] == '\0' || nextnlat == 16) {
369
      fprintf(fd, "\r\n");
369
      fprintf(fd, "\r\n");
370
      nextnlat = 0;
370
      nextnlat = 0;
371
    }
371
    }
372
  }
372
  }
373
  fprintf(fd, "};\r\n\r\n");
373
  fprintf(fd, "};\r\n\r\n");
374
 
374
 
375
  fprintf(fd, "unsigned short svarlang_dict[%u] = {\r\n", l->num_strings * 2);
375
  fprintf(fd, "unsigned short svarlang_dict[%u] = {\r\n", l->num_strings * 2);
376
  for (i = 0; i < l->num_strings; i++) {
376
  for (i = 0; i < l->num_strings; i++) {
377
    if (!fprintf(fd, "0x%04x,0x%04x", l->dict[i].id, l->dict[i].offset)) {
377
    if (!fprintf(fd, "0x%04x,0x%04x", l->dict[i].id, l->dict[i].offset)) {
378
      fclose(fd);
378
      fclose(fd);
379
      return(0);
379
      return(0);
380
    }
380
    }
381
    if (i + 1 < l->num_strings) fprintf(fd, ",");
381
    if (i + 1 < l->num_strings) fprintf(fd, ",");
382
    fprintf(fd, "\r\n");
382
    fprintf(fd, "\r\n");
383
  }
383
  }
384
  fprintf(fd, "};\r\n");
384
  fprintf(fd, "};\r\n");
385
 
385
 
386
  fclose(fd);
386
  fclose(fd);
387
 
387
 
388
  return(1);
388
  return(1);
389
}
389
}
390
 
390
 
391
 
391
 
392
int main(int argc, char **argv) {
392
int main(int argc, char **argv) {
393
  FILE *fd;
393
  FILE *fd;
394
  int ecode = 0;
394
  int ecode = 0;
395
  int i;
395
  int i;
396
  unsigned short biggest_langsz = 0;
396
  unsigned short biggest_langsz = 0;
397
  svl_lang_t *lang, *reflang = NULL;
397
  struct svl_lang *lang, *reflang = NULL;
398
 
398
 
399
  if (argc < 2) {
399
  if (argc < 2) {
400
    puts("tlumacz ver " SVARLANGVER " - this tool is part of the SvarLANG project.");
400
    puts("tlumacz ver " SVARLANGVER " - this tool is part of the SvarLANG project.");
401
    puts("converts a set of CATS-style translations in files EN.TXT, PL.TXT, etc");
401
    puts("converts a set of CATS-style translations in files EN.TXT, PL.TXT, etc");
402
    puts("into a single resource file (OUT.LNG).");
402
    puts("into a single resource file (OUT.LNG).");
403
    puts("");
403
    puts("");
404
    puts("usage: tlumacz en fr pl ...");
404
    puts("usage: tlumacz en fr pl ...");
405
    return(1);
405
    return(1);
406
  }
406
  }
407
 
407
 
408
  fd = fopen("out.lng", "wb");
408
  fd = fopen("out.lng", "wb");
409
  if (fd == NULL) {
409
  if (fd == NULL) {
410
    puts("ERR: failed to open or create OUT.LNG");
410
    puts("ERR: failed to open or create OUT.LNG");
411
    return(1);
411
    return(1);
412
  }
412
  }
413
 
413
 
414
  /* write lang blocks */
414
  /* write lang blocks */
415
  for (i = 1; i < argc; i++) {
415
  for (i = 1; i < argc; i++) {
416
    unsigned short sz;
416
    unsigned short sz;
417
    char id[3];
417
    char id[3];
418
 
418
 
419
    if (strlen(argv[i]) != 2) {
419
    if (strlen(argv[i]) != 2) {
420
      printf("INVALID LANG SPECIFIED: %s\r\n", argv[i]);
420
      printf("INVALID LANG SPECIFIED: %s\r\n", argv[i]);
421
      ecode = 1;
421
      ecode = 1;
422
      break;
422
      break;
423
    }
423
    }
424
    id[0] = argv[i][0];
424
    id[0] = argv[i][0];
425
    id[1] = argv[i][1];
425
    id[1] = argv[i][1];
426
    id[2] = 0;
426
    id[2] = 0;
427
 
427
 
428
    if ((lang = svl_lang_new(id, DICT_CAP, STRINGS_CAP)) == NULL) {
428
    if ((lang = svl_lang_new(id, DICT_CAP, STRINGS_CAP)) == NULL) {
429
      printf("OUT OF MEMORY\r\n");
429
      printf("OUT OF MEMORY\r\n");
430
      return(1);
430
      return(1);
431
    }
431
    }
432
 
432
 
433
    sz = svl_lang_from_cats_file(lang, reflang);
433
    sz = svl_lang_from_cats_file(lang, reflang);
434
    if (sz == 0) {
434
    if (sz == 0) {
435
      printf("ERROR COMPUTING LANG '%s'\r\n", id);
435
      printf("ERROR COMPUTING LANG '%s'\r\n", id);
436
      ecode = 1;
436
      ecode = 1;
437
      break;
437
      break;
438
    } else {
438
    } else {
439
      printf("computed %s lang block of %u bytes\r\n", id, sz);
439
      printf("computed %s lang block of %u bytes\r\n", id, sz);
440
      if (sz > biggest_langsz) biggest_langsz = sz;
440
      if (sz > biggest_langsz) biggest_langsz = sz;
441
    }
441
    }
442
    svl_compact_lang(lang);
442
    svl_compact_lang(lang);
443
 
443
 
444
    /* write header if first (reference) language */
444
    /* write header if first (reference) language */
445
    if (i == 1) {
445
    if (i == 1) {
446
      if (!svl_write_header(lang->num_strings, fd)) {
446
      if (!svl_write_header(lang->num_strings, fd)) {
447
        printf("ERROR WRITING TO OUTPUT FILE\r\n");
447
        printf("ERROR WRITING TO OUTPUT FILE\r\n");
448
        ecode = 1;
448
        ecode = 1;
449
        break;
449
        break;
450
      }
450
      }
451
    }
451
    }
452
 
452
 
453
    /* write lang ID to file, followed string table size, and then
453
    /* write lang ID to file, followed string table size, and then
454
       the dictionary and string table for current language */
454
       the dictionary and string table for current language */
455
    if (!svl_write_lang(lang, fd)) {
455
    if (!svl_write_lang(lang, fd)) {
456
      printf("ERROR WRITING TO OUTPUT FILE\r\n");
456
      printf("ERROR WRITING TO OUTPUT FILE\r\n");
457
      ecode = 1;
457
      ecode = 1;
458
      break;
458
      break;
459
    }
459
    }
460
 
460
 
461
    /* remember reference data for other languages */
461
    /* remember reference data for other languages */
462
    if (i == 1) {
462
    if (i == 1) {
463
      reflang = lang;
463
      reflang = lang;
464
    } else {
464
    } else {
465
      svl_lang_free(lang);
465
      svl_lang_free(lang);
466
      lang = NULL;
466
      lang = NULL;
467
    }
467
    }
468
  }
468
  }
469
 
469
 
470
  /* compute the deflang.c file containing a dump of the reference block */
470
  /* compute the deflang.c file containing a dump of the reference block */
471
  if (!svl_write_c_source(reflang, "deflang.c", biggest_langsz)) {
471
  if (!svl_write_c_source(reflang, "deflang.c", biggest_langsz)) {
472
    puts("ERROR: FAILED TO OPEN OR CREATE DEFLANG.C");
472
    puts("ERROR: FAILED TO OPEN OR CREATE DEFLANG.C");
473
    ecode = 1;
473
    ecode = 1;
474
  }
474
  }
475
 
475
 
476
  /* clean up */
476
  /* clean up */
477
  if (reflang) {
477
  if (reflang) {
478
    svl_lang_free(reflang);
478
    svl_lang_free(reflang);
479
    reflang = NULL;
479
    reflang = NULL;
480
  }
480
  }
481
 
481
 
482
  return(ecode);
482
  return(ecode);
483
}
483
}
484
 
484