Subversion Repositories SvarDOS

Rev

Rev 2084 | Rev 2089 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
597 mateuszvis 1
/*
2083 mateusz.vi 2
 * Copyright (C) 2021-2024 Mateusz Viste
597 mateuszvis 3
 *
1295 mateusz.vi 4
 * Dictionary-based lookups contributed by Bernd Boeckmann, 2023
5
 *
597 mateuszvis 6
 * usage: tlumacz en fr pl etc
7
 *
2083 mateusz.vi 8
 * computes:
9
 * OUT.LNG -> contains all language resources.
10
 * OUTC.LNG -> same as OUT.LNG but with compressed strings (slower to load).
597 mateuszvis 11
 *
2083 mateusz.vi 12
 * === COMPRESSION ===========================================================
13
 * The compression scheme is very simple. It is applied only to strings (ie.
14
 * not the dictionnary) and it is basically a stream of 16-bit values (WORDs),
15
 * where each WORD value contains the following bits "LLLL OOOO OOOO OOOO":
16
 *
17
 * OOOO OOOO OOOO = a backreference offset ("look that many bytes back")
18
 * LLLL = the number of bytes to copy from the backreference
19
 *
20
 * Special case: a WORD that is smaller than 256 represents a single literal
21
 * byte.
22
 *
23
 * To recognize a compressed lang block one has to look at the id of the block
24
 * (16-bit language id). If its highest bit is set (0x8000) then the lang block
25
 * is compressed.
597 mateuszvis 26
 */
27
 
28
 
29
#include <stdio.h>
30
#include <stdlib.h>
31
#include <string.h>
1290 bernd.boec 32
#include <ctype.h>
597 mateuszvis 33
 
1248 mateusz.vi 34
#include "svarlang.h"
597 mateuszvis 35
 
1290 bernd.boec 36
#define STRINGS_CAP 65000   /* string storage size in characters */
1293 mateusz.vi 37
#define DICT_CAP    10000   /* dictionary size in elements */
597 mateuszvis 38
 
2014 bernd.boec 39
enum {                      /* DEFLANG output format */
40
  C_OUTPUT,
41
  ASM_OUTPUT,
42
  NASM_OUTPUT
43
};
44
 
45
 
597 mateuszvis 46
/* read a single line from fd and fills it into dst, returns line length
47
 * ending CR/LF is trimmed, as well as any trailing spaces */
48
static unsigned short readl(char *dst, size_t dstsz, FILE *fd) {
49
  unsigned short l, lastnonspace = 0;
50
 
1290 bernd.boec 51
  if (fgets(dst, (int)dstsz, fd) == NULL) return(0xffff); /* EOF */
597 mateuszvis 52
  /* trim at first CR or LF and return len */
53
  for (l = 0; (dst[l] != 0) && (dst[l] != '\r') && (dst[l] != '\n'); l++) {
54
    if (dst[l] != ' ') lastnonspace = l;
55
  }
56
 
57
  if (lastnonspace < l) l = lastnonspace + 1; /* rtrim */
58
  dst[l] = 0;
59
 
60
  return(l);
61
}
62
 
63
 
1114 mateusz.vi 64
/* parse a line in format "[?]1.50:somestring". fills id and returns a pointer to
597 mateuszvis 65
 * the actual string part on success, or NULL on error */
1114 mateusz.vi 66
static const char *parseline(unsigned short *id, const char *s) {
597 mateuszvis 67
  int i;
68
  int dotpos = 0, colpos = 0, gotdigits = 0;
69
 
1114 mateusz.vi 70
  /* strings prefixed by '?' are flagged as "dirty": ignore this flag here */
71
  if (*s == '?') s++;
72
 
597 mateuszvis 73
  /* I must have a . and a : in the first 9 bytes */
74
  for (i = 0;; i++) {
75
    if (s[i] == '.') {
76
      if ((dotpos != 0) || (gotdigits == 0)) break;
77
      dotpos = i;
78
      gotdigits = 0;
79
    } else if (s[i] == ':') {
80
      if (gotdigits != 0) colpos = i;
81
      break;
82
    } else if ((s[i] < '0') || (s[i] > '9')) {
83
      break;
84
    }
85
    gotdigits++;
86
  }
87
  /* did I collect everything? */
88
  if ((dotpos == 0) || (colpos == 0)) return(NULL);
89
 
90
  *id = atoi(s);
91
  *id <<= 8;
92
  *id |= atoi(s + dotpos + 1);
93
 
94
  /* printf("parseline(): %04X = '%s'\r\n", *id, s + colpos + 1); */
95
 
96
  return(s + colpos + 1);
97
}
98
 
99
 
639 mateusz.vi 100
/* converts escape sequences like "\n" or "\t" into actual bytes, returns
101
 * the new length of the string. */
102
static unsigned short unesc_string(char *linebuff) {
103
  unsigned short i;
104
  for (i = 0; linebuff[i] != 0; i++) {
105
    if (linebuff[i] != '\\') continue;
1290 bernd.boec 106
    memmove(linebuff + i, linebuff + i + 1, strlen(linebuff + i));
639 mateusz.vi 107
    if (linebuff[i] == 0) break;
108
    switch (linebuff[i]) {
1248 mateusz.vi 109
      case 'e':
110
        linebuff[i] = 0x1B; /* ESC code, using hex because '\e' is not ANSI C */
111
        break;
639 mateusz.vi 112
      case 'n':
113
        linebuff[i] = '\n';
114
        break;
115
      case 'r':
116
        linebuff[i] = '\r';
117
        break;
118
      case 't':
119
        linebuff[i] = '\t';
120
        break;
121
    }
122
  }
123
  return(i);
124
}
125
 
1290 bernd.boec 126
#pragma pack(1)
1296 mateusz.vi 127
struct dict_entry {
1295 mateusz.vi 128
  unsigned short id;
129
  unsigned short offset;
1296 mateusz.vi 130
};
1290 bernd.boec 131
#pragma pack()
639 mateusz.vi 132
 
1296 mateusz.vi 133
struct svl_lang {
1290 bernd.boec 134
  char id[2];
135
  unsigned short num_strings;
136
 
1296 mateusz.vi 137
  struct dict_entry *dict;
1290 bernd.boec 138
  size_t dict_cap;
139
 
140
  char *strings;
141
  char *strings_end;
142
  size_t strings_cap;
143
 
1296 mateusz.vi 144
};
1290 bernd.boec 145
 
146
 
1296 mateusz.vi 147
static struct svl_lang *svl_lang_new(const char langid[2], size_t dict_cap, size_t strings_cap) {
148
  struct svl_lang *l;
1290 bernd.boec 149
 
1296 mateusz.vi 150
  l = malloc(sizeof(struct svl_lang));
1295 mateusz.vi 151
  if (!l) return(NULL);
1290 bernd.boec 152
 
153
  l->id[0] = (char)toupper(langid[0]);
154
  l->id[1] = (char)toupper(langid[1]);
155
 
1296 mateusz.vi 156
  l->dict = malloc(dict_cap * sizeof(struct dict_entry));
1295 mateusz.vi 157
  if (!l->dict) return(NULL);
158
 
1290 bernd.boec 159
  l->dict_cap = dict_cap;
160
 
161
  l->num_strings = 0;
162
  l->strings = l->strings_end = malloc(strings_cap);
163
  if (!l->strings) {
164
    free(l->dict);
1295 mateusz.vi 165
    return(NULL);
1290 bernd.boec 166
  }
167
  l->strings_cap = strings_cap;
1295 mateusz.vi 168
 
169
  return(l);
1290 bernd.boec 170
}
171
 
172
 
173
/* compacts the dict and string buffer */
1296 mateusz.vi 174
static void svl_compact_lang(struct svl_lang *l) {
1290 bernd.boec 175
  size_t bytes;
176
  bytes = l->strings_end - l->strings;
177
  if (bytes < l->strings_cap) {
178
    l->strings = l->strings_end = realloc(l->strings, bytes);
179
    l->strings_end += bytes;
180
    l->strings_cap = bytes;
181
  }
182
  l->dict_cap = l->num_strings;
1296 mateusz.vi 183
  l->dict = realloc(l->dict, l->dict_cap * sizeof(struct dict_entry));
1290 bernd.boec 184
}
185
 
186
 
1296 mateusz.vi 187
static void svl_lang_free(struct svl_lang *l) {
1290 bernd.boec 188
  l->num_strings = 0;
189
  if (l->dict) {
190
    free(l->dict);
191
    l->dict = NULL;
192
  }
193
  if (l->strings) {
194
    free(l->strings);
195
    l->strings = l->strings_end = NULL;
196
  }
197
  l->dict_cap = 0;
198
  l->strings_cap = 0;
199
}
200
 
201
 
1296 mateusz.vi 202
static size_t svl_strings_bytes(const struct svl_lang *l) {
1295 mateusz.vi 203
  return(l->strings_end - l->strings);
1290 bernd.boec 204
}
205
 
206
 
1296 mateusz.vi 207
static size_t svl_dict_bytes(const struct svl_lang *l) {
208
  return(l->num_strings * sizeof(struct dict_entry));
1290 bernd.boec 209
}
210
 
211
 
1296 mateusz.vi 212
static int svl_add_str(struct svl_lang *l, unsigned short id, const char *s) {
1290 bernd.boec 213
  size_t len = strlen(s) + 1;
214
  size_t cursor;
215
 
1296 mateusz.vi 216
  if ((l->strings_cap < svl_strings_bytes(l) + len) || (l->dict_cap < (l->num_strings + 1) * sizeof(struct dict_entry))) {
1295 mateusz.vi 217
    return(0);
1290 bernd.boec 218
  }
1293 mateusz.vi 219
 
1290 bernd.boec 220
  /* find dictionary insert position, search backwards in assumption
221
     that in translation files, strings are generally ordered ascending */
222
  for (cursor = l->num_strings; cursor > 0 && l->dict[cursor-1].id > id; cursor--);
223
 
1296 mateusz.vi 224
  memmove(&(l->dict[cursor+1]), &(l->dict[cursor]), sizeof(struct dict_entry) * (l->num_strings - cursor));
1290 bernd.boec 225
  l->dict[cursor].id = id;
226
  l->dict[cursor].offset = l->strings_end - l->strings;
227
 
228
  memcpy(l->strings_end, s, len);
229
  l->strings_end += len;
230
  l->num_strings++;
231
 
1295 mateusz.vi 232
  return(1);
1290 bernd.boec 233
}
234
 
235
 
1296 mateusz.vi 236
static int svl_find(const struct svl_lang *l, unsigned short id) {
1295 mateusz.vi 237
  size_t left = 0, right = l->num_strings - 1, x;
238
  unsigned short v;
1290 bernd.boec 239
 
1295 mateusz.vi 240
  if (l->num_strings == 0) return(0);
1290 bernd.boec 241
 
1295 mateusz.vi 242
  while (left <= right ) {
243
    x = left + ( (right - left ) >> 2 );
244
    v = l->dict[x].id;
1296 mateusz.vi 245
    if ( id == v ) return(1); /* found! */
246
 
247
    if (id > v) {
1295 mateusz.vi 248
      left = x + 1;
249
    } else {
250
      right = x - 1;
251
    }
252
  }
253
  return(0);
1290 bernd.boec 254
}
255
 
1295 mateusz.vi 256
 
1061 mateusz.vi 257
/* opens a CATS-style file and compiles it into a ressources lang block
258
 * returns 0 on error, or the size of the generated data block otherwise */
1296 mateusz.vi 259
static unsigned short svl_lang_from_cats_file(struct svl_lang *l, struct svl_lang *refl) {
1290 bernd.boec 260
  unsigned short linelen;
597 mateuszvis 261
  FILE *fd;
1290 bernd.boec 262
  char fname[] = "xx.txt";
623 mateuszvis 263
  static char linebuf[8192];
1114 mateusz.vi 264
  const char *ptr;
1290 bernd.boec 265
  unsigned short id, maxid=0, maxid_line, linecount;
266
  int i;
597 mateuszvis 267
 
1290 bernd.boec 268
  fname[strlen(fname) - 6] = (char)tolower( l->id[0] );
269
  fname[strlen(fname) - 5] = (char)tolower( l->id[1] );
597 mateuszvis 270
 
271
  fd = fopen(fname, "rb");
272
  if (fd == NULL) {
273
    printf("ERROR: FAILED TO OPEN '%s'\r\n", fname);
274
    return(0);
275
  }
276
 
277
  for (linecount = 1;; linecount++) {
278
    linelen = readl(linebuf, sizeof(linebuf), fd);
279
    if (linelen == 0xffff) break; /* EOF */
280
    if ((linelen == 0) || (linebuf[0] == '#')) continue;
281
 
639 mateusz.vi 282
    /* convert escaped chars to actual bytes (\n -> newline, etc) */
283
    linelen = unesc_string(linebuf);
284
 
597 mateuszvis 285
    /* read id and get ptr to actual string ("1.15:string") */
286
    ptr = parseline(&id, linebuf);
1272 mateusz.vi 287
 
288
    /* handle malformed lines */
597 mateuszvis 289
    if (ptr == NULL) {
1272 mateusz.vi 290
      printf("WARNING: %s[#%u] is malformed (linelen = %u):\r\n", fname, linecount, linelen);
623 mateuszvis 291
      puts(linebuf);
1272 mateusz.vi 292
      continue;
597 mateuszvis 293
    }
1272 mateusz.vi 294
 
295
    /* ignore empty strings (but emit a warning) */
296
    if (ptr[0] == 0) {
1271 bernd.boec 297
      printf("WARNING: %s[#%u] ignoring empty string %u.%u\r\n", fname, linecount, id >> 8, id & 0xff);
298
      continue;
299
    }
597 mateuszvis 300
 
1114 mateusz.vi 301
    /* warn about dirty lines */
302
    if (linebuf[0] == '?') {
303
      printf("WARNING: %s[#%u] string id %u.%u is flagged as 'dirty'\r\n", fname, linecount, id >> 8, id & 0xff);
304
    }
305
 
1290 bernd.boec 306
    /* add the string contained in current line, if conditions are met */
307
    if (!svl_find(l, id)) {
1295 mateusz.vi 308
      if ((refl == NULL) || (svl_find(refl, id))) {
1290 bernd.boec 309
        if (!svl_add_str(l, id, ptr)) {
2014 bernd.boec 310
          fprintf(stderr, "ERROR: %s[#%u] output size limit exceeded\r\n", fname, linecount);
1290 bernd.boec 311
          fclose(fd);
1295 mateusz.vi 312
          return(0);
1290 bernd.boec 313
        }
314
        if (id >= maxid) {
315
          maxid = id;
316
          maxid_line = linecount;
1295 mateusz.vi 317
        } else {
1293 mateusz.vi 318
          printf("WARNING:%s[#%u] file unsorted - line %u has higher id %u.%u\r\n", fname, linecount, maxid_line, maxid >> 8, maxid & 0xff);
1290 bernd.boec 319
        }
1295 mateusz.vi 320
      } else {
1290 bernd.boec 321
        printf("WARNING: %s[#%u] has an invalid id (%u.%u not present in ref lang)\r\n", fname, linecount, id >> 8, id & 0xff);
322
      }
1295 mateusz.vi 323
    } else {
1293 mateusz.vi 324
      printf("WARNING: %s[#%u] has a duplicated id (%u.%u)\r\n", fname, linecount, id >> 8, id & 0xff);
597 mateuszvis 325
    }
326
  }
327
 
328
  fclose(fd);
329
 
1290 bernd.boec 330
  /* if reflang provided, pull missing strings from it */
331
  if (refl != NULL) {
332
    for (i = 0; i < refl->num_strings; i++) {
333
      id = refl->dict[i].id;
334
      if (!svl_find(l, id)) {
597 mateuszvis 335
        printf("WARNING: %s is missing string %u.%u (pulled from ref lang)\r\n", fname, id >> 8, id & 0xff);
1291 bernd.boec 336
        if (!svl_add_str(l, id, refl->strings + refl->dict[i].offset)) {
2014 bernd.boec 337
          fprintf(stderr, "ERROR: %s[#%u] output size limit exceeded\r\n", fname, linecount);
1295 mateusz.vi 338
          return(0);
1290 bernd.boec 339
        }
597 mateuszvis 340
      }
341
    }
342
  }
343
 
1290 bernd.boec 344
  return(svl_strings_bytes(l));
345
}
597 mateuszvis 346
 
1290 bernd.boec 347
 
1295 mateusz.vi 348
static int svl_write_header(unsigned short num_strings, FILE *fd) {
349
  return((fwrite("SvL\x1a", 1, 4, fd) == 4) && (fwrite(&num_strings, 1, 2, fd) == 2));
597 mateuszvis 350
}
351
 
352
 
2083 mateusz.vi 353
/* mvcomp applies the MV-COMPRESSION algorithm to data and returns the compressed size */
354
static unsigned short mvcomp(char *dstbuf, const char *src, unsigned short len) {
355
  unsigned short complen = 0;
356
  unsigned short *dst = (void *)dstbuf;
357
  unsigned short bytesprocessed = 0;
358
 
359
  /* read src byte by byte, len times, each time look for a match of 15,14,13..2 chars in the back buffer */
360
  while (len > 0) {
361
    unsigned short matchlen;
362
    unsigned short offset;
2084 mateusz.vi 363
    matchlen = 16;
2083 mateusz.vi 364
    if (len < matchlen) matchlen = len;
365
 
366
    for (; matchlen > 1; matchlen--) {
2086 mateusz.vi 367
      /* start at -matchlen and try to match something moving backward */
368
      unsigned short maxoffset = 4096;
369
      if (maxoffset > bytesprocessed) maxoffset = bytesprocessed;
2083 mateusz.vi 370
 
2086 mateusz.vi 371
      for (offset = matchlen; offset <= maxoffset; offset++) {
2083 mateusz.vi 372
        if (memcmp(src, src - offset, matchlen) == 0) {
2086 mateusz.vi 373
          //printf("Found match of %u bytes at offset -%u: '%c%c%c...'\n", matchlen, offset, src[0], src[1], src[2]);
2083 mateusz.vi 374
          goto FOUND;
375
        }
376
      }
377
    }
378
 
379
    /* if here: no match found, write a literal byte */
380
    *dst = *src;
381
    dst++;
382
    src++;
383
    bytesprocessed++;
384
    len--;
385
    complen += 2;
386
    continue;
387
 
388
    FOUND: /* found a match of matchlen bytes at -offset */
2084 mateusz.vi 389
    *dst = ((matchlen - 1) << 12) | (offset - 1);
2083 mateusz.vi 390
    dst++;
391
    src += matchlen;
392
    bytesprocessed += matchlen;
393
    len -= matchlen;
394
    complen += 2;
395
  }
396
 
397
  return(complen);
398
}
399
 
400
 
401
/* write the language block (id, dict, strings) into the LNG file.
402
 * strings are compressed if compflag != 0 */
403
static int svl_write_lang(const struct svl_lang *l, FILE *fd, int compflag) {
1290 bernd.boec 404
  unsigned short strings_bytes = svl_strings_bytes(l);
2083 mateusz.vi 405
  unsigned short langid = *((unsigned short *)(&l->id));
406
  const char *stringsptr = l->strings;
597 mateuszvis 407
 
2083 mateusz.vi 408
  /* if compressed then do the magic */
409
  if (compflag) {
410
    static char compstrings[65000];
411
    langid |= 0x8000; /* LNG langblock flag that means "this lang is compressed" */
412
    strings_bytes = mvcomp(compstrings, l->strings, strings_bytes);
413
    stringsptr = compstrings;
414
  }
415
 
416
  return((fwrite(&langid, 1, 2, fd) == 2) &&
1290 bernd.boec 417
         (fwrite(&strings_bytes, 1, 2, fd) == 2) &&
418
         (fwrite(l->dict, 1, svl_dict_bytes(l), fd) == svl_dict_bytes(l)) &&
2083 mateusz.vi 419
         (fwrite(stringsptr, 1, strings_bytes, fd) == strings_bytes));
1290 bernd.boec 420
}
421
 
422
 
1296 mateusz.vi 423
static int svl_write_c_source(const struct svl_lang *l, const char *fn, unsigned short biggest_langsz) {
1290 bernd.boec 424
  FILE *fd;
425
  int i;
426
  unsigned short strings_bytes = svl_strings_bytes(l);
427
  unsigned short nextnlat = 0;
1295 mateusz.vi 428
  unsigned short allocsz;
1290 bernd.boec 429
 
430
  fd = fopen(fn, "wb");
431
  if (fd == NULL) {
1295 mateusz.vi 432
    return(0);
433
  }
1290 bernd.boec 434
 
1295 mateusz.vi 435
  allocsz = biggest_langsz + (biggest_langsz / 20);
436
  printf("biggest lang block is %u bytes -> allocating a %u bytes buffer (5%% safety margin)\n", biggest_langsz, allocsz);
437
  fprintf(fd, "/* THIS FILE HAS BEEN GENERATED BY TLUMACZ (PART OF THE SVARLANG LIBRARY) */\r\n");
438
  fprintf(fd, "const unsigned short svarlang_memsz = %uu;\r\n", allocsz);
439
  fprintf(fd, "const unsigned short svarlang_string_count = %uu;\r\n\r\n", l->num_strings);
440
  fprintf(fd, "char svarlang_mem[%u] = {\r\n", allocsz);
441
 
442
  for (i = 0; i < strings_bytes; i++) {
443
    if (!fprintf(fd, "0x%02x", l->strings[i])) {
444
      fclose(fd);
445
      return(0);
1290 bernd.boec 446
    }
447
 
1295 mateusz.vi 448
    if (i + 1 < strings_bytes) fprintf(fd, ",");
449
    nextnlat++;
450
    if (l->strings[i] == '\0' || nextnlat == 16) {
1290 bernd.boec 451
      fprintf(fd, "\r\n");
1295 mateusz.vi 452
      nextnlat = 0;
1290 bernd.boec 453
    }
1295 mateusz.vi 454
  }
455
  fprintf(fd, "};\r\n\r\n");
1290 bernd.boec 456
 
1295 mateusz.vi 457
  fprintf(fd, "unsigned short svarlang_dict[%u] = {\r\n", l->num_strings * 2);
458
  for (i = 0; i < l->num_strings; i++) {
459
    if (!fprintf(fd, "0x%04x,0x%04x", l->dict[i].id, l->dict[i].offset)) {
460
      fclose(fd);
461
      return(0);
462
    }
463
    if (i + 1 < l->num_strings) fprintf(fd, ",");
464
    fprintf(fd, "\r\n");
1290 bernd.boec 465
  }
1295 mateusz.vi 466
  fprintf(fd, "};\r\n");
1290 bernd.boec 467
 
1295 mateusz.vi 468
  fclose(fd);
469
 
470
  return(1);
1290 bernd.boec 471
}
472
 
473
 
2014 bernd.boec 474
static int svl_write_asm_source(const struct svl_lang *l, const char *fn, unsigned short biggest_langsz, int format) {
475
  FILE *fd;
476
  int i;
477
  unsigned short strings_bytes = svl_strings_bytes(l);
478
  unsigned short nextnlat = 0;
479
  unsigned short allocsz;
480
 
481
  const char *public = (format == ASM_OUTPUT) ? "public" : "global";
482
 
483
  fd = fopen(fn, "wb");
484
  if (fd == NULL) {
485
    return(0);
486
  }
487
 
488
  allocsz = biggest_langsz + (biggest_langsz / 20);
489
  printf("biggest lang block is %u bytes -> allocating a %u bytes buffer (5%% safety margin)\n", biggest_langsz, allocsz);
490
  fprintf(fd, "; THIS FILE HAS BEEN GENERATED BY TLUMACZ (PART OF THE SVARLANG LIBRARY)\r\n");
491
  fprintf(fd, "%s svarlang_memsz\r\n", public);
492
  fprintf(fd, "svarlang_memsz dw %u\r\n", allocsz);
493
  fprintf(fd, "%s svarlang_string_count\r\n", public);
494
  fprintf(fd, "svarlang_string_count dw %u\r\n\r\n", l->num_strings);
495
  fprintf(fd, "%s svarlang_mem\r\n", public);
496
  fprintf(fd, "svarlang_mem:\r\n");
497
 
498
  if (strings_bytes > 0) fprintf(fd, "db ");
499
 
500
  for (i = 0; i < strings_bytes; i++) {
2016 bernd.boec 501
    if (!fprintf(fd, "%u", l->strings[i])) {
2014 bernd.boec 502
      fclose(fd);
503
      return(0);
504
    }
505
 
506
    nextnlat++;
507
    if (l->strings[i] == '\0' || nextnlat == 16) {
508
      fprintf(fd, "\r\n");
509
      if (i + 1 < strings_bytes ) fprintf(fd, "db ");
510
      nextnlat = 0;
511
    }
512
    else {
513
      fprintf(fd, ",");
514
    }
515
  }
516
 
517
  fprintf(fd, "\r\n%s svarlang_dict\r\n", public);
518
  fprintf(fd, "svarlang_dict:\r\n");
519
  for (i = 0; i < l->num_strings; i++) {
2016 bernd.boec 520
    if (!fprintf(fd, "dw %u,%u\r\n", l->dict[i].id, l->dict[i].offset)) {
2014 bernd.boec 521
      fclose(fd);
522
      return(0);
523
    }
524
  }
525
 
526
  fclose(fd);
527
 
528
  return(1);
529
}
530
 
531
 
597 mateuszvis 532
int main(int argc, char **argv) {
2083 mateusz.vi 533
  FILE *fd, *fdc;
597 mateuszvis 534
  int ecode = 0;
2014 bernd.boec 535
  int i, output_format = C_OUTPUT;
1061 mateusz.vi 536
  unsigned short biggest_langsz = 0;
2015 bernd.boec 537
  struct svl_lang *lang = NULL, *reflang = NULL;
597 mateuszvis 538
 
539
  if (argc < 2) {
1247 mateusz.vi 540
    puts("tlumacz ver " SVARLANGVER " - this tool is part of the SvarLANG project.");
541
    puts("converts a set of CATS-style translations in files EN.TXT, PL.TXT, etc");
542
    puts("into a single resource file (OUT.LNG).");
543
    puts("");
2014 bernd.boec 544
    puts("usage: tlumacz [/c|/asm|/nasm] en fr pl ...");
597 mateuszvis 545
    return(1);
546
  }
547
 
601 mateuszvis 548
  fd = fopen("out.lng", "wb");
597 mateuszvis 549
  if (fd == NULL) {
2014 bernd.boec 550
    fprintf(stderr, "ERROR: FAILED TO CREATE OR OPEN OUT.LNG");
597 mateuszvis 551
    return(1);
552
  }
2083 mateusz.vi 553
  fdc = fopen("outc.lng", "wb");
554
  if (fd == NULL) {
555
    fclose(fd);
556
    puts("ERR: failed to open or create OUTC.LNG");
557
    return(1);
558
  }
597 mateuszvis 559
 
560
  /* write lang blocks */
561
  for (i = 1; i < argc; i++) {
562
    unsigned short sz;
563
    char id[3];
564
 
2014 bernd.boec 565
    if (!strcmp(argv[i], "/c")) {
566
      output_format = C_OUTPUT;
567
      continue;
568
    }
569
    else if (!strcmp(argv[i], "/asm")) {
570
      output_format = ASM_OUTPUT;
571
      continue;
572
    } else if(!strcmp(argv[i], "/nasm")) {
573
      output_format = NASM_OUTPUT;
574
      continue;
575
    }
576
 
597 mateuszvis 577
    if (strlen(argv[i]) != 2) {
2014 bernd.boec 578
      fprintf(stderr, "INVALID LANG SPECIFIED: %s\r\n", argv[i]);
597 mateuszvis 579
      ecode = 1;
2015 bernd.boec 580
      goto exit_main;
597 mateuszvis 581
    }
582
    id[0] = argv[i][0];
583
    id[1] = argv[i][1];
584
    id[2] = 0;
585
 
1290 bernd.boec 586
    if ((lang = svl_lang_new(id, DICT_CAP, STRINGS_CAP)) == NULL) {
2014 bernd.boec 587
      fprintf(stderr, "OUT OF MEMORY\r\n");
2015 bernd.boec 588
      ecode = 1;
589
      goto exit_main;
1290 bernd.boec 590
    }
591
 
592
    sz = svl_lang_from_cats_file(lang, reflang);
597 mateuszvis 593
    if (sz == 0) {
2014 bernd.boec 594
      fprintf(stderr, "ERROR COMPUTING LANG '%s'\r\n", id);
597 mateuszvis 595
      ecode = 1;
2015 bernd.boec 596
      goto exit_main;
597 mateuszvis 597
    } else {
598
      printf("computed %s lang block of %u bytes\r\n", id, sz);
1061 mateusz.vi 599
      if (sz > biggest_langsz) biggest_langsz = sz;
597 mateuszvis 600
    }
1290 bernd.boec 601
    svl_compact_lang(lang);
602
 
603
    /* write header if first (reference) language */
604
    if (i == 1) {
605
      if (!svl_write_header(lang->num_strings, fd)) {
2014 bernd.boec 606
        fprintf(stderr, "ERROR WRITING TO OUTPUT FILE\r\n");
1290 bernd.boec 607
        ecode = 1;
2015 bernd.boec 608
        goto exit_main;
1290 bernd.boec 609
      }
2083 mateusz.vi 610
      if (!svl_write_header(lang->num_strings, fdc)) {
611
        fprintf(stderr, "ERROR WRITING TO OUTPUT (COMPRESSED) FILE\r\n");
612
        ecode = 1;
613
        break;
614
      }
1290 bernd.boec 615
    }
1293 mateusz.vi 616
 
1290 bernd.boec 617
    /* write lang ID to file, followed string table size, and then
618
       the dictionary and string table for current language */
2083 mateusz.vi 619
    if (!svl_write_lang(lang, fd, 0)) { /* UNCOMPRESSED */
2014 bernd.boec 620
      fprintf(stderr, "ERROR WRITING TO OUTPUT FILE\r\n");
597 mateuszvis 621
      ecode = 1;
2015 bernd.boec 622
      goto exit_main;
597 mateuszvis 623
    }
1290 bernd.boec 624
 
2083 mateusz.vi 625
    /* write lang ID to file, followed string table size, and then
626
       the dictionary and string table for current language */
627
    if (!svl_write_lang(lang, fdc, 1)) { /* COMPRESSED */
628
      fprintf(stderr, "ERROR WRITING TO OUTPUT (COMPRESSED) FILE\r\n");
629
      ecode = 1;
630
      goto exit_main;
631
    }
632
 
1061 mateusz.vi 633
    /* remember reference data for other languages */
2014 bernd.boec 634
    if (!reflang) {
1290 bernd.boec 635
      reflang = lang;
1295 mateusz.vi 636
    } else {
1290 bernd.boec 637
      svl_lang_free(lang);
638
      lang = NULL;
639
    }
597 mateuszvis 640
  }
641
 
2014 bernd.boec 642
  if (!reflang) {
643
    fprintf(stderr, "ERROR: NO LANGUAGE GIVEN\r\n");
2015 bernd.boec 644
    ecode = 1;
645
    goto exit_main;
1061 mateusz.vi 646
  }
647
 
2014 bernd.boec 648
  /* compute the deflang file containing a dump of the reference block */
649
  if (output_format == C_OUTPUT) {
650
    if (!svl_write_c_source(reflang, "deflang.c", biggest_langsz)) {
651
      fprintf(stderr, "ERROR: FAILED TO OPEN OR CREATE DEFLANG.C\r\n");
652
      ecode = 1;
2083 mateusz.vi 653
    }
2014 bernd.boec 654
  } else {
655
    if (!svl_write_asm_source(reflang, "deflang.inc", biggest_langsz, output_format)) {
656
      fprintf(stderr, "ERROR: FAILED TO OPEN OR CREATE DEFLANG.INC\r\n");
657
      ecode = 1;
658
    }
1290 bernd.boec 659
  }
660
 
2015 bernd.boec 661
exit_main:
662
  if (lang && lang != reflang) {
663
    svl_lang_free(lang);
664
  }
665
  if (reflang) {
666
    svl_lang_free(reflang);
667
    reflang = NULL;
2083 mateusz.vi 668
    lang = NULL;
2015 bernd.boec 669
  }
2014 bernd.boec 670
 
2015 bernd.boec 671
  fclose(fd);
672
 
597 mateuszvis 673
  return(ecode);
674
}