Subversion Repositories SvarDOS

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
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
 *
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
 
31
 
32
 
33
struct bitmap {
34
  unsigned char bits[8192];
35
};
36
 
37
static void bitmap_set(struct bitmap *b, unsigned short id) {
623 mateuszvis 38
  b->bits[id >> 3] |= 1 << (id & 7);
597 mateuszvis 39
}
40
 
41
static int bitmap_get(const struct bitmap *b, unsigned short id) {
623 mateuszvis 42
  return(b->bits[id >> 3] & (1 << (id & 7)));
597 mateuszvis 43
}
44
 
45
static void bitmap_init(struct bitmap *b) {
623 mateuszvis 46
  bzero(b, sizeof(struct bitmap));
597 mateuszvis 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";
623 mateuszvis 108
  static char linebuf[8192];
597 mateuszvis 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) {
623 mateuszvis 131
      printf("ERROR: line #%u of %s is malformed (linelen = %u):\r\n", linecount, fname, linelen);
132
      puts(linebuf);
597 mateuszvis 133
      len = 0;
134
      break;
135
    }
136
 
623 mateuszvis 137
    /* write string into block (II LL S) */
597 mateuszvis 138
    memcpy(buff + len, &id, 2);
139
    len += 2;
623 mateuszvis 140
    {
141
      unsigned short slen = strlen(ptr) + 1;
142
      memcpy(buff + len, &slen, 2);
143
      len += 2;
144
      memcpy(buff + len, ptr, slen);
145
      len += slen;
146
    }
597 mateuszvis 147
 
148
    /* if reference bitmap provided: check that the id is valid */
149
    if ((refb != NULL) && (bitmap_get(refb, id) == 0)) {
150
      printf("WARNING: %s[#%u] has an invalid id (%u.%u not present in ref lang)\r\n", fname, linecount, id >> 8, id & 0xff);
151
    }
152
 
153
    /* make sure this id is not already present */
154
    if (bitmap_get(b, id) == 0) {
155
      /* set bit in bitmap to remember I have this string */
156
      bitmap_set(b, id);
157
    } else {
158
      printf("WARNING: %s[#%u] has a duplicated id (%u.%u)\r\n", fname, linecount, id >> 8, id & 0xff);
159
    }
160
  }
161
 
162
  fclose(fd);
163
 
164
  /* if refblock provided, pull missing strings from it */
165
  if (refblock != NULL) {
166
    for (;;) {
623 mateuszvis 167
      unsigned short slen;
168
      id = ((unsigned short *)refblock)[0];
169
      slen = ((unsigned short *)refblock)[1];
170
      if ((id == 0) && (slen == 0)) break;
597 mateuszvis 171
      if (bitmap_get(b, id) == 0) {
172
        printf("WARNING: %s is missing string %u.%u (pulled from ref lang)\r\n", fname, id >> 8, id & 0xff);
173
        /* copy missing string from refblock */
623 mateuszvis 174
        memcpy(buff + len, refblock, slen + 4);
175
        len += slen + 4;
597 mateuszvis 176
      }
623 mateuszvis 177
      refblock += slen + 4;
597 mateuszvis 178
    }
179
  }
180
 
181
  /* write the block terminator (0-long string) */
182
  buff[len++] = 0; /* id */
183
  buff[len++] = 0; /* id */
184
  buff[len++] = 0; /* len */
623 mateuszvis 185
  buff[len++] = 0; /* len */
186
  buff[len++] = 0; /* empty string */
597 mateuszvis 187
 
188
  return(len);
189
}
190
 
191
 
599 mateuszvis 192
#define MEMBLOCKSZ 65000
597 mateuszvis 193
 
194
int main(int argc, char **argv) {
195
  FILE *fd;
196
  int ecode = 0;
197
  char *buff, *refblock;
198
  static struct bitmap bufbitmap;
199
  static struct bitmap refbitmap;
200
  unsigned short i;
201
 
202
  if (argc < 2) {
203
    puts("usage: tlumacz en fr pl etc");
204
    return(1);
205
  }
206
 
207
  buff = malloc(MEMBLOCKSZ);
208
  refblock = malloc(MEMBLOCKSZ);
209
  if ((buff == NULL) || (refblock == NULL)) {
210
    puts("out of memory");
211
    return(1);
212
  }
213
 
601 mateuszvis 214
  fd = fopen("out.lng", "wb");
597 mateuszvis 215
  if (fd == NULL) {
216
    puts("ERR: failed to open or create SVARCOM.LNG");
217
    return(1);
218
  }
219
 
220
  /* write sig */
221
  fwrite("SvL\x1b", 1, 4, fd);
222
 
223
  /* write lang blocks */
224
  for (i = 1; i < argc; i++) {
225
    unsigned short sz;
226
    char id[3];
227
 
228
    if (strlen(argv[i]) != 2) {
229
      printf("INVALID LANG SPECIFIED: %s\r\n", argv[i]);
230
      ecode = 1;
231
      break;
232
    }
233
 
234
    id[0] = argv[i][0];
235
    id[1] = argv[i][1];
236
    id[2] = 0;
237
    if (id[0] >= 'a') id[0] -= 'a' - 'A';
238
    if (id[1] >= 'a') id[1] -= 'a' - 'A';
239
 
240
    sz = gen_langstrings(buff, id, &bufbitmap, (i != 1)?&refbitmap:NULL, (i != 1)?refblock:NULL);
241
    if (sz == 0) {
242
      printf("ERROR COMPUTING LANG '%s'\r\n", id);
243
      ecode = 1;
244
      break;
245
    } else {
246
      printf("computed %s lang block of %u bytes\r\n", id, sz);
247
    }
248
    /* write lang ID to file, followed by block size and then the actual block */
249
    if ((fwrite(id, 1, 2, fd) != 2) ||
250
        (fwrite(&sz, 1, 2, fd) != 2) ||
251
        (fwrite(buff, 1, sz, fd) != sz)) {
252
      printf("ERROR WRITING TO OUTPUT FILE\r\n");
253
      ecode = 1;
254
      break;
255
    }
256
    /* compute the default block for reference language */
257
    if (i == 1) {
258
      unsigned short x;
259
      FILE *fd2;
260
      fd2 = fopen("DEFLANG.C", "wb");
261
      if (fd2 == NULL) {
262
        puts("ERROR: FAILED TO OPEN OR CREATE DEFLANG.C");
263
        break;
264
      }
265
      fprintf(fd2, "/* THIS FILE HAS BEEN AUTOGENERATE BY TLUMACZ (PART OF THE SVARLANG LIBRARY) */\r\n");
599 mateuszvis 266
      fprintf(fd2, "const unsigned short svarlang_memsz = %uu;\r\n", sz * 2);
267
      fprintf(fd2, "char svarlang_mem[%u] = {\r\n", sz * 2);
597 mateuszvis 268
      for (x = 0; x < sz; x++) {
269
        fprintf(fd2, "%u", buff[x]);
270
        if (x + 1 < sz) fprintf(fd2, ",");
271
        if ((x & 15) == 15) fprintf(fd2, "\r\n");
272
      }
273
      fprintf(fd2, "};\r\n");
274
      fclose(fd2);
275
      /* remember reference data for other languages */
276
      memcpy(refblock, buff, MEMBLOCKSZ);
277
      memcpy(&refbitmap, &bufbitmap, sizeof(struct bitmap));
278
    }
279
  }
280
 
281
  fclose(fd);
282
 
283
  return(ecode);
284
}