Subversion Repositories SvarDOS

Rev

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

Rev Author Line No. Line
597 mateuszvis 1
/*
1247 mateusz.vi 2
 * Copyright (C) 2021-2023 Mateusz Viste
597 mateuszvis 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
 *
623 mateuszvis 19
 * II LL S  where II is the string's 16-bit identifier, LL is its length
20
 *          (1-65535) and S is the actual string. All strings are ASCIIZ (ie.
21
 *          they end with a NULL terminator).
597 mateuszvis 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
 
1248 mateusz.vi 31
#include "svarlang.h"
597 mateuszvis 32
 
33
 
34
struct bitmap {
35
  unsigned char bits[8192];
36
};
37
 
38
static void bitmap_set(struct bitmap *b, unsigned short id) {
623 mateuszvis 39
  b->bits[id >> 3] |= 1 << (id & 7);
597 mateuszvis 40
}
41
 
42
static int bitmap_get(const struct bitmap *b, unsigned short id) {
623 mateuszvis 43
  return(b->bits[id >> 3] & (1 << (id & 7)));
597 mateuszvis 44
}
45
 
46
static void bitmap_init(struct bitmap *b) {
623 mateuszvis 47
  bzero(b, sizeof(struct bitmap));
597 mateuszvis 48
}
49
 
50
 
51
 
52
/* read a single line from fd and fills it into dst, returns line length
53
 * ending CR/LF is trimmed, as well as any trailing spaces */
54
static unsigned short readl(char *dst, size_t dstsz, FILE *fd) {
55
  unsigned short l, lastnonspace = 0;
56
 
57
  if (fgets(dst, dstsz, fd) == NULL) return(0xffff); /* EOF */
58
  /* trim at first CR or LF and return len */
59
  for (l = 0; (dst[l] != 0) && (dst[l] != '\r') && (dst[l] != '\n'); l++) {
60
    if (dst[l] != ' ') lastnonspace = l;
61
  }
62
 
63
  if (lastnonspace < l) l = lastnonspace + 1; /* rtrim */
64
  dst[l] = 0;
65
 
66
  return(l);
67
}
68
 
69
 
1114 mateusz.vi 70
/* parse a line in format "[?]1.50:somestring". fills id and returns a pointer to
597 mateuszvis 71
 * the actual string part on success, or NULL on error */
1114 mateusz.vi 72
static const char *parseline(unsigned short *id, const char *s) {
597 mateuszvis 73
  int i;
74
  int dotpos = 0, colpos = 0, gotdigits = 0;
75
 
1114 mateusz.vi 76
  /* strings prefixed by '?' are flagged as "dirty": ignore this flag here */
77
  if (*s == '?') s++;
78
 
597 mateuszvis 79
  /* I must have a . and a : in the first 9 bytes */
80
  for (i = 0;; i++) {
81
    if (s[i] == '.') {
82
      if ((dotpos != 0) || (gotdigits == 0)) break;
83
      dotpos = i;
84
      gotdigits = 0;
85
    } else if (s[i] == ':') {
86
      if (gotdigits != 0) colpos = i;
87
      break;
88
    } else if ((s[i] < '0') || (s[i] > '9')) {
89
      break;
90
    }
91
    gotdigits++;
92
  }
93
  /* did I collect everything? */
94
  if ((dotpos == 0) || (colpos == 0)) return(NULL);
95
  if (s[colpos + 1] == 0) return(NULL);
96
 
97
  *id = atoi(s);
98
  *id <<= 8;
99
  *id |= atoi(s + dotpos + 1);
100
 
101
  /* printf("parseline(): %04X = '%s'\r\n", *id, s + colpos + 1); */
102
 
103
  return(s + colpos + 1);
104
}
105
 
106
 
639 mateusz.vi 107
/* converts escape sequences like "\n" or "\t" into actual bytes, returns
108
 * the new length of the string. */
109
static unsigned short unesc_string(char *linebuff) {
110
  unsigned short i;
111
  for (i = 0; linebuff[i] != 0; i++) {
112
    if (linebuff[i] != '\\') continue;
113
    strcpy(linebuff + i, linebuff + i + 1);
114
    if (linebuff[i] == 0) break;
115
    switch (linebuff[i]) {
1248 mateusz.vi 116
      case 'e':
117
        linebuff[i] = 0x1B; /* ESC code, using hex because '\e' is not ANSI C */
118
        break;
639 mateusz.vi 119
      case 'n':
120
        linebuff[i] = '\n';
121
        break;
122
      case 'r':
123
        linebuff[i] = '\r';
124
        break;
125
      case 't':
126
        linebuff[i] = '\t';
127
        break;
128
    }
129
  }
130
  return(i);
131
}
132
 
133
 
1061 mateusz.vi 134
/* opens a CATS-style file and compiles it into a ressources lang block
135
 * returns 0 on error, or the size of the generated data block otherwise */
597 mateuszvis 136
static unsigned short gen_langstrings(unsigned char *buff, const char *langid, struct bitmap *b, const struct bitmap *refb, const unsigned char *refblock) {
137
  unsigned short len = 0, linelen;
138
  FILE *fd;
139
  char fname[] = "XX.TXT";
623 mateuszvis 140
  static char linebuf[8192];
1114 mateusz.vi 141
  const char *ptr;
597 mateuszvis 142
  unsigned short id, linecount;
143
 
144
  bitmap_init(b);
145
 
146
  memcpy(fname + strlen(fname) - 6, langid, 2);
147
 
148
  fd = fopen(fname, "rb");
149
  if (fd == NULL) {
150
    printf("ERROR: FAILED TO OPEN '%s'\r\n", fname);
151
    return(0);
152
  }
153
 
154
  for (linecount = 1;; linecount++) {
155
 
156
    linelen = readl(linebuf, sizeof(linebuf), fd);
157
    if (linelen == 0xffff) break; /* EOF */
158
    if ((linelen == 0) || (linebuf[0] == '#')) continue;
159
 
639 mateusz.vi 160
    /* convert escaped chars to actual bytes (\n -> newline, etc) */
161
    linelen = unesc_string(linebuf);
162
 
597 mateuszvis 163
    /* read id and get ptr to actual string ("1.15:string") */
164
    ptr = parseline(&id, linebuf);
165
    if (ptr == NULL) {
623 mateuszvis 166
      printf("ERROR: line #%u of %s is malformed (linelen = %u):\r\n", linecount, fname, linelen);
167
      puts(linebuf);
597 mateuszvis 168
      len = 0;
169
      break;
170
    }
171
 
1114 mateusz.vi 172
    /* warn about dirty lines */
173
    if (linebuf[0] == '?') {
174
      printf("WARNING: %s[#%u] string id %u.%u is flagged as 'dirty'\r\n", fname, linecount, id >> 8, id & 0xff);
175
    }
176
 
623 mateuszvis 177
    /* write string into block (II LL S) */
597 mateuszvis 178
    memcpy(buff + len, &id, 2);
179
    len += 2;
623 mateuszvis 180
    {
181
      unsigned short slen = strlen(ptr) + 1;
182
      memcpy(buff + len, &slen, 2);
183
      len += 2;
184
      memcpy(buff + len, ptr, slen);
185
      len += slen;
186
    }
597 mateuszvis 187
 
188
    /* if reference bitmap provided: check that the id is valid */
189
    if ((refb != NULL) && (bitmap_get(refb, id) == 0)) {
190
      printf("WARNING: %s[#%u] has an invalid id (%u.%u not present in ref lang)\r\n", fname, linecount, id >> 8, id & 0xff);
191
    }
192
 
193
    /* make sure this id is not already present */
194
    if (bitmap_get(b, id) == 0) {
195
      /* set bit in bitmap to remember I have this string */
196
      bitmap_set(b, id);
197
    } else {
198
      printf("WARNING: %s[#%u] has a duplicated id (%u.%u)\r\n", fname, linecount, id >> 8, id & 0xff);
199
    }
200
  }
201
 
202
  fclose(fd);
203
 
204
  /* if refblock provided, pull missing strings from it */
205
  if (refblock != NULL) {
206
    for (;;) {
623 mateuszvis 207
      unsigned short slen;
208
      id = ((unsigned short *)refblock)[0];
209
      slen = ((unsigned short *)refblock)[1];
210
      if ((id == 0) && (slen == 0)) break;
597 mateuszvis 211
      if (bitmap_get(b, id) == 0) {
212
        printf("WARNING: %s is missing string %u.%u (pulled from ref lang)\r\n", fname, id >> 8, id & 0xff);
213
        /* copy missing string from refblock */
623 mateuszvis 214
        memcpy(buff + len, refblock, slen + 4);
215
        len += slen + 4;
597 mateuszvis 216
      }
623 mateuszvis 217
      refblock += slen + 4;
597 mateuszvis 218
    }
219
  }
220
 
221
  /* write the block terminator (0-long string) */
222
  buff[len++] = 0; /* id */
223
  buff[len++] = 0; /* id */
224
  buff[len++] = 0; /* len */
623 mateuszvis 225
  buff[len++] = 0; /* len */
226
  buff[len++] = 0; /* empty string */
597 mateuszvis 227
 
228
  return(len);
229
}
230
 
231
 
599 mateuszvis 232
#define MEMBLOCKSZ 65000
597 mateuszvis 233
 
234
int main(int argc, char **argv) {
235
  FILE *fd;
236
  int ecode = 0;
237
  char *buff, *refblock;
1061 mateusz.vi 238
  unsigned short refblocksz = 0;
597 mateuszvis 239
  static struct bitmap bufbitmap;
240
  static struct bitmap refbitmap;
241
  unsigned short i;
1061 mateusz.vi 242
  unsigned short biggest_langsz = 0;
597 mateuszvis 243
 
244
  if (argc < 2) {
1247 mateusz.vi 245
    puts("tlumacz ver " SVARLANGVER " - this tool is part of the SvarLANG project.");
246
    puts("converts a set of CATS-style translations in files EN.TXT, PL.TXT, etc");
247
    puts("into a single resource file (OUT.LNG).");
248
    puts("");
249
    puts("usage: tlumacz en fr pl ...");
597 mateuszvis 250
    return(1);
251
  }
252
 
253
  buff = malloc(MEMBLOCKSZ);
254
  refblock = malloc(MEMBLOCKSZ);
255
  if ((buff == NULL) || (refblock == NULL)) {
256
    puts("out of memory");
257
    return(1);
258
  }
259
 
601 mateuszvis 260
  fd = fopen("out.lng", "wb");
597 mateuszvis 261
  if (fd == NULL) {
1250 mateusz.vi 262
    puts("ERR: failed to open or create OUT.LNG");
597 mateuszvis 263
    return(1);
264
  }
265
 
266
  /* write sig */
267
  fwrite("SvL\x1b", 1, 4, fd);
268
 
269
  /* write lang blocks */
270
  for (i = 1; i < argc; i++) {
271
    unsigned short sz;
272
    char id[3];
273
 
274
    if (strlen(argv[i]) != 2) {
275
      printf("INVALID LANG SPECIFIED: %s\r\n", argv[i]);
276
      ecode = 1;
277
      break;
278
    }
279
 
280
    id[0] = argv[i][0];
281
    id[1] = argv[i][1];
282
    id[2] = 0;
283
    if (id[0] >= 'a') id[0] -= 'a' - 'A';
284
    if (id[1] >= 'a') id[1] -= 'a' - 'A';
285
 
286
    sz = gen_langstrings(buff, id, &bufbitmap, (i != 1)?&refbitmap:NULL, (i != 1)?refblock:NULL);
287
    if (sz == 0) {
288
      printf("ERROR COMPUTING LANG '%s'\r\n", id);
289
      ecode = 1;
290
      break;
291
    } else {
292
      printf("computed %s lang block of %u bytes\r\n", id, sz);
1061 mateusz.vi 293
      if (sz > biggest_langsz) biggest_langsz = sz;
597 mateuszvis 294
    }
295
    /* write lang ID to file, followed by block size and then the actual block */
296
    if ((fwrite(id, 1, 2, fd) != 2) ||
297
        (fwrite(&sz, 1, 2, fd) != 2) ||
298
        (fwrite(buff, 1, sz, fd) != sz)) {
299
      printf("ERROR WRITING TO OUTPUT FILE\r\n");
300
      ecode = 1;
301
      break;
302
    }
1061 mateusz.vi 303
    /* remember reference data for other languages */
597 mateuszvis 304
    if (i == 1) {
1061 mateusz.vi 305
      refblocksz = sz;
597 mateuszvis 306
      memcpy(refblock, buff, MEMBLOCKSZ);
307
      memcpy(&refbitmap, &bufbitmap, sizeof(struct bitmap));
308
    }
309
  }
310
 
311
  fclose(fd);
312
 
1061 mateusz.vi 313
  /* compute the deflang.c file containing a dump of the reference block */
314
  fd = fopen("DEFLANG.C", "wb");
315
  if (fd == NULL) {
316
    puts("ERROR: FAILED TO OPEN OR CREATE DEFLANG.C");
317
    ecode = 1;
318
  } else {
319
    unsigned short allocsz = biggest_langsz + (biggest_langsz / 20);
1251 mateusz.vi 320
    unsigned short nextstringin = 0, nextnlat = 40;
321
    printf("biggest lang block is %u bytes -> allocating a %u bytes buffer (5%% safety margin)\n", biggest_langsz, allocsz);
1061 mateusz.vi 322
    fprintf(fd, "/* THIS FILE HAS BEEN GENERATED BY TLUMACZ (PART OF THE SVARLANG LIBRARY) */\r\n");
323
    fprintf(fd, "const unsigned short svarlang_memsz = %uu;\r\n", allocsz);
1251 mateusz.vi 324
    fprintf(fd, "char svarlang_mem[%u] = {", allocsz);
1061 mateusz.vi 325
    for (i = 0; i < refblocksz; i++) {
1251 mateusz.vi 326
      if (nextstringin == 0) {
327
        fprintf(fd, "\r\n");
328
        nextnlat = i + 40;
329
        nextstringin = 4 + (refblock[i + 3] << 8) + refblock[i + 2];
330
        if (nextstringin == 4) nextstringin = 20000; /* last string in block */
331
      }
332
      if (i == nextnlat) {
333
        nextnlat += 40;
334
        fprintf(fd, "\r\n");
335
      }
336
      nextnlat--;
337
      nextstringin--;
1064 mateusz.vi 338
      fprintf(fd, "%u", refblock[i]);
1061 mateusz.vi 339
      if (i + 1 < refblocksz) fprintf(fd, ",");
340
    }
341
    fprintf(fd, "};\r\n");
342
    fclose(fd);
343
  }
344
 
597 mateuszvis 345
  return(ecode);
346
}