Subversion Repositories SvarDOS

Rev

Rev 2121 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1290 bernd.boec 1
/* This file is part of the svarlang project and is published under the terms
2
 * of the MIT license.
3
 *
1779 mateusz.vi 4
 * Copyright (C) 2021-2024 Mateusz Viste
1290 bernd.boec 5
 *
6
 * Permission is hereby granted, free of charge, to any person obtaining a
7
 * copy of this software and associated documentation files (the "Software"),
8
 * to deal in the Software without restriction, including without limitation
9
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10
 * and/or sell copies of the Software, and to permit persons to whom the
11
 * Software is furnished to do so, subject to the following conditions:
12
 *
13
 * The above copyright notice and this permission notice shall be included in
14
 * all copies or substantial portions of the Software.
15
 *
16
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22
 * DEALINGS IN THE SOFTWARE.
23
 */
24
 
25
/* if WITHSTDIO is enabled, then remap file operations to use the standard
26
 * stdio amenities */
27
#ifdef WITHSTDIO
28
 
29
#include <stdio.h>   /* FILE, fopen(), fseek(), etc */
30
typedef FILE* FHANDLE;
31
#define FOPEN(x) fopen(x, "rb")
32
#define FCLOSE(x) fclose(x)
33
#define FSEEK(f,b) fseek(f,b,SEEK_CUR)
34
#define FREAD(f,t,b) fread(t, 1, b, f)
35
 
36
#else
37
 
1300 mateusz.vi 38
#include <i86.h>  /* FP_SEG, FP_OFF */
1290 bernd.boec 39
typedef unsigned short FHANDLE;
40
 
41
#endif
42
 
43
 
44
#include "svarlang.h"
45
 
46
 
47
/* supplied through DEFLANG.C */
48
extern char svarlang_mem[];
49
extern unsigned short svarlang_dict[];
50
extern const unsigned short svarlang_memsz;
51
extern const unsigned short svarlang_string_count;
52
 
1298 mateusz.vi 53
 
1290 bernd.boec 54
const char *svarlang_strid(unsigned short id) {
1299 mateusz.vi 55
  unsigned short left = 0, right = svarlang_string_count - 1, x;
1298 mateusz.vi 56
  unsigned short v;
1290 bernd.boec 57
 
1298 mateusz.vi 58
  if (svarlang_string_count == 0) return("");
1290 bernd.boec 59
 
1298 mateusz.vi 60
  while (left <= right) {
1299 mateusz.vi 61
    x = left + ((right - left ) >> 2);
1298 mateusz.vi 62
    v = svarlang_dict[x * 2];
63
 
64
    if (id == v) return(svarlang_mem + svarlang_dict[x * 2 + 1]);
65
 
66
    if (id > v) {
2007 mateusz.vi 67
      if (x == 65535) goto not_found;
68
      left = x + 1;
1298 mateusz.vi 69
    } else {
2007 mateusz.vi 70
      if (x == 0) goto not_found;
71
      right = x - 1;
1298 mateusz.vi 72
    }
73
  }
74
 
2005 bernd.boec 75
not_found:
1298 mateusz.vi 76
  return("");
1290 bernd.boec 77
}
78
 
1293 mateusz.vi 79
 
1290 bernd.boec 80
/* routines below are simplified (dos-based) versions of the libc FILE-related
81
 * functions. Using them avoids a dependency on FILE, hence makes the binary
1779 mateusz.vi 82
 * smaller if the application does not need to pull fopen() and friends
83
 * I use pragma aux directives for more compact size. open-watcom only. */
1290 bernd.boec 84
#ifndef WITHSTDIO
85
 
1781 mateusz.vi 86
static unsigned short FOPEN(const char far *s);
1290 bernd.boec 87
 
1779 mateusz.vi 88
#pragma aux FOPEN = \
89
"push ds" \
1781 mateusz.vi 90
"push es" \
91
"pop ds" \
1779 mateusz.vi 92
"mov ax, 0x3D00" /* open file, read-only (fname at DS:DX) */ \
93
"int 0x21" \
94
"jnc DONE" \
1783 mateusz.vi 95
"xor ax, ax"     /* return 0 on error */ \
1779 mateusz.vi 96
"DONE:" \
97
"pop ds" \
1781 mateusz.vi 98
parm [es dx] \
1779 mateusz.vi 99
value [ax];
1290 bernd.boec 100
 
101
 
1780 mateusz.vi 102
static void FCLOSE(unsigned short handle);
1290 bernd.boec 103
 
1780 mateusz.vi 104
#pragma aux FCLOSE = \
105
"mov ah, 0x3E" \
106
"int 0x21" \
107
modify [ax]  /* AX might contain an error code on failure */ \
108
parm [bx]
1290 bernd.boec 109
 
1780 mateusz.vi 110
 
1782 mateusz.vi 111
static unsigned short FREAD(unsigned short handle, void far *buff, unsigned short bytes);
1290 bernd.boec 112
 
1782 mateusz.vi 113
#pragma aux FREAD = \
114
"push ds" \
115
"push es" \
116
"pop ds" \
117
"mov ah, 0x3F"    /* read cx bytes from file handle bx to DS:DX */ \
118
"int 0x21" \
119
"jnc ERR" \
120
"xor ax, ax"      /* return 0 on error */ \
121
"ERR:" \
122
"pop ds" \
123
parm [bx] [es dx] [cx] \
124
value [ax]
1290 bernd.boec 125
 
126
 
1781 mateusz.vi 127
static void FSEEK(unsigned short handle, unsigned short bytes);
128
 
129
#pragma aux FSEEK = \
130
"mov ax, 0x4201"  /* move file pointer from cur pos + CX:DX */ \
131
"xor cx, cx" \
132
"int 0x21" \
133
parm [bx] [dx] \
134
modify [ax cx dx]
135
 
1290 bernd.boec 136
#endif
137
 
1293 mateusz.vi 138
 
2118 mateusz.vi 139
 
140
#ifdef MVUCOMP_ASM
141
 
2119 mateusz.vi 142
static void mvucomp_asm(unsigned short bufseg, unsigned short dst, unsigned short src, unsigned short complen);
2118 mateusz.vi 143
 
2119 mateusz.vi 144
#pragma aux mvucomp_asm = \
145
"    push ds"\
146
\
147
     /* ds:si = compressed stream */     \
148
     /* es:di = output location */       \
149
     /* bx = len of compressed stream */ \
150
"    shr bx, 1" /* convert byte length to number of words */ \
151
"    cld" /* make sure stosw and friends move forward */ \
152
     /* set ds = es = bufseg */          \
153
"    push es"\
154
"    pop ds"\
155
\
2121 mateusz.vi 156
"    xor dx, dx" /* literal continuation counter */ \
2119 mateusz.vi 157
\
158
"    AGAIN:"\
159
\
160
     /* do I have any input? */ \
161
"    test bx, bx"\
162
"    jz KONIEC"\
163
"    dec bx"\
164
\
165
     /* load token */ \
166
"    lodsw"  /* mov ax, [ds:si] + inc si + inc si */ \
167
\
168
"    /* literal continuation? */"\
2121 mateusz.vi 169
"    test dx, dx"\
2119 mateusz.vi 170
"    jz TRY_BACKREF"\
171
"    stosw"\
2121 mateusz.vi 172
"    dec dx" /* a byte shorter than dec dl */ \
2119 mateusz.vi 173
"    jmp AGAIN"\
174
\
175
/* back ref? */ \
176
"    TRY_BACKREF:"\
177
"    test ax, 0xf000"\
178
"    jz LITERAL_START" /* else it's a literal start */ \
179
     /* AH = LLLL OOOO   AL = OOOO OOOO */ \
180
     /* copy (ah>>4)+1 bytes from (ax & 0x0FFF)+1 */ \
181
     /* save regs */ \
182
"    push si"\
183
     /* prep DS:SI = source ; ES:DI = destination ; CX = len */ \
2121 mateusz.vi 184
"    mov ch, ah" /* this is all about setting CX to the high nibble of AX */ \
185
"    mov cl, 4"  /* using a code as compact as possible.                  */ \
2119 mateusz.vi 186
"    shr ch, cl"\
187
"    mov cl, ch"\
188
"    xor ch, ch"\
189
"    inc cx"\
190
"    and ax, 0x0fff"  /* clear the backref length bits */ \
191
"    inc ax"\
192
"    mov si, di"\
193
"    sub si, ax"\
194
"    /* do the copy */"\
2120 mateusz.vi 195
"    rep movsb" /* copy cx bytes from ds:si to es:di + inc si + inc di */ \
2119 mateusz.vi 196
\
197
"    /* restore regs */"\
198
"    pop si"\
199
"    jmp AGAIN"\
200
\
201
"    LITERAL_START:" /* write al to dst and set literal counter */ \
202
     /* 0000 UUUU  BBBB BBBB */ \
203
"    stosb" /* mov [es:di], al + inc di */ \
204
"    mov dl, ah" /* ah high nibble is guaranteed to be zero */ \
205
"    jmp AGAIN"\
206
""\
207
"    KONIEC:"\
208
"    pop ds"\
209
modify [ax bx cx dx di si] \
210
parm [es] [di] [si] [bx]
2118 mateusz.vi 211
 
212
#else
213
 
214
void mvucomp(char *dst, const unsigned short *src, unsigned short complen) {
215
  unsigned char rawwords = 0; /* number of uncompressible words */
216
  complen /= 2; /* I'm interested in number of words, not bytes */
217
 
218
  while (complen != 0) {
219
    unsigned short token;
220
    /* get next mvcomp token */
221
    token = *src;
222
    src++;
223
    complen--;
224
 
225
    /* token format is LLLL OOOO OOOO OOOO, where:
226
     * OOOO OOOO OOOO is the back reference offset (number of bytes-1 to rewind)
227
     * LLLL is the number of bytes (-1) that have to be copied from the offset.
228
     *
229
     * However, if LLLL is zero then the token's format is different:
230
     * 0000 RRRR BBBB BBBB
231
     *
232
     * The above form occurs when uncompressible data is encountered:
233
     * BBBB BBBB is the literal value of a byte to be copied
234
     * RRRR is the number of RAW (uncompressible) WORDS that follow (possibly 0)
235
     */
236
 
237
    /* raw word? */
238
    if (rawwords != 0) {
239
      unsigned short *dst16 = (void *)dst;
240
      *dst16 = token;
241
      dst += 2;
242
      rawwords--;
243
 
244
    /* literal byte? */
245
    } else if ((token & 0xF000) == 0) {
246
      *dst = token; /* no need for an explicit "& 0xff", dst is a char ptr so token is naturally truncated to lowest 8 bits */
247
      dst++;
248
      rawwords = token >> 8; /* number of RAW words that are about to follow */
249
 
250
    /* else it's a backreference */
251
    } else {
252
      char *src = dst - (token & 0x0FFF) - 1;
253
      token >>= 12;
254
      for (;;) {
255
        *dst = *src;
256
        dst++;
257
        src++;
258
        if (token == 0) break;
259
        token--;
260
      }
261
    }
262
  }
263
}
264
 
265
#endif
266
 
267
 
1290 bernd.boec 268
int svarlang_load(const char *fname, const char *lang) {
269
  unsigned short langid;
270
  unsigned short buff16[2];
271
  FHANDLE fd;
1376 mateusz.vi 272
  signed char exitcode = 0;
1373 mateusz.vi 273
  struct {
274
    unsigned long sig;
275
    unsigned short string_count;
276
  } hdr;
1290 bernd.boec 277
 
278
  langid = *((unsigned short *)lang);
279
  langid &= 0xDFDF; /* make sure lang is upcase */
280
 
281
  fd = FOPEN(fname);
282
  if (!fd) return(-1);
283
 
1373 mateusz.vi 284
  /* read hdr, sig should be "SvL\x1a" (0x1a4c7653) */
285
  if ((FREAD(fd, &hdr, 6) != 6) || (hdr.sig != 0x1a4c7653L) || (hdr.string_count != svarlang_string_count)) {
1376 mateusz.vi 286
    exitcode = -2;
287
    goto FCLOSE_AND_EXIT;
1290 bernd.boec 288
  }
289
 
1374 mateusz.vi 290
  for (;;) {
291
    /* read next lang id and string table size in file */
292
    if (FREAD(fd, buff16, 4) != 4) {
1376 mateusz.vi 293
      exitcode = -3;
294
      goto FCLOSE_AND_EXIT;
1374 mateusz.vi 295
    }
1290 bernd.boec 296
 
297
    /* is it the lang I am looking for? */
2083 mateusz.vi 298
    if ((buff16[0] & 0x7FFF) == langid) break; /* compare without highest bit - it is a flag for compression */
1290 bernd.boec 299
 
1375 mateusz.vi 300
    /* skip to next lang (in two steps to avoid a potential uint16 overflow) */
1374 mateusz.vi 301
    FSEEK(fd, svarlang_string_count * 4);
302
    FSEEK(fd, buff16[1]);
303
  }
1290 bernd.boec 304
 
2083 mateusz.vi 305
  /* load the index (dict) */
306
  if (FREAD(fd, svarlang_dict, svarlang_string_count * 4) != svarlang_string_count * 4) {
1376 mateusz.vi 307
    exitcode = -4;
2083 mateusz.vi 308
    goto FCLOSE_AND_EXIT;
1290 bernd.boec 309
  }
310
 
2083 mateusz.vi 311
  /* is the lang block compressed? then uncompress it */
312
  if (buff16[0] & 0x8000) {
2115 mateusz.vi 313
    unsigned short *mvcompptr;
2102 mateusz.vi 314
 
2115 mateusz.vi 315
    /* start by loading the entire block at the end of the svarlang mem */
316
    mvcompptr = (void *)(svarlang_mem + svarlang_memsz - buff16[1]);
317
    if (FREAD(fd, mvcompptr, buff16[1]) != buff16[1]) {
318
      exitcode = -5;
319
      goto FCLOSE_AND_EXIT;
320
    }
321
 
322
    /* uncompress now */
2118 mateusz.vi 323
#ifndef MVUCOMP_ASM
324
    mvucomp(svarlang_mem, mvcompptr, buff16[1]);
325
#else
326
    mvucomp_asm(FP_SEG(svarlang_mem), FP_OFF(svarlang_mem), FP_OFF(svarlang_mem) + svarlang_memsz - buff16[1], buff16[1]);
327
#endif
2083 mateusz.vi 328
 
329
    goto FCLOSE_AND_EXIT;
330
  }
331
 
332
  /* lang block not compressed - load as is */
333
  if (FREAD(fd, svarlang_mem, buff16[1]) != buff16[1]) {
2115 mateusz.vi 334
    exitcode = -7;
2083 mateusz.vi 335
  }
336
 
1376 mateusz.vi 337
  FCLOSE_AND_EXIT:
338
 
1290 bernd.boec 339
  FCLOSE(fd);
1376 mateusz.vi 340
  return(exitcode);
1290 bernd.boec 341
}