Subversion Repositories SvarDOS

Rev

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

Rev Author Line No. Line
421 mateuszvis 1
/* This file is part of the SvarCOM project and is published under the terms
2
 * of the MIT license.
3
 *
4
 * Copyright (C) 2021 Mateusz Viste
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
 
352 mateuszvis 25
/*
26
 * a variety of helper functions
27
 * Copyright (C) 2021 Mateusz Viste
28
 */
29
 
396 mateuszvis 30
#include <i86.h>    /* MK_FP() */
420 mateuszvis 31
#include <stdio.h>  /* sprintf() */
437 mateuszvis 32
#include <string.h> /* memcpy() */
396 mateuszvis 33
 
437 mateuszvis 34
#include "deflang.h"
35
 
36
#include "env.h"
37
 
352 mateuszvis 38
#include "helpers.h"
39
 
420 mateuszvis 40
 
352 mateuszvis 41
/* case-insensitive comparison of strings, returns non-zero on equality */
42
int imatch(const char *s1, const char *s2) {
43
  for (;;) {
44
    char c1, c2;
45
    c1 = *s1;
46
    c2 = *s2;
47
    if ((c1 >= 'a') && (c1 <= 'z')) c1 -= ('a' - 'A');
48
    if ((c2 >= 'a') && (c2 <= 'z')) c2 -= ('a' - 'A');
49
    /* */
50
    if (c1 != c2) return(0);
51
    if (c1 == 0) return(1);
52
    s1++;
53
    s2++;
54
  }
55
}
56
 
57
 
58
/* returns zero if s1 starts with s2 */
59
int strstartswith(const char *s1, const char *s2) {
60
  while (*s2 != 0) {
61
    if (*s1 != *s2) return(-1);
62
    s1++;
63
    s2++;
64
  }
65
  return(0);
66
}
369 mateuszvis 67
 
68
 
69
/* outputs a NULL-terminated string to stdout */
70
void output_internal(const char *s, unsigned short nl) {
71
  _asm {
72
    mov ah, 0x02 /* AH=9 - write character in DL to stdout */
73
    mov si, s
74
    cld          /* clear DF so lodsb increments SI */
75
    NEXTBYTE:
76
    lodsb /* load byte from DS:SI into AL, SI++ */
77
    mov dl, al
78
    or al, 0  /* is al == 0? */
79
    jz DONE
80
    int 0x21
81
    jmp NEXTBYTE
82
    DONE:
83
    or nl, 0
84
    jz FINITO
85
    /* print out a CR/LF trailer if nl set */
86
    mov dl, 0x0D /* CR */
87
    int 0x21
88
    mov dl, 0x0A /* LF */
89
    int 0x21
90
    FINITO:
91
  }
92
}
388 mateuszvis 93
 
94
 
437 mateuszvis 95
void nls_output_internal(unsigned short id, unsigned short nl) {
96
  const char *ptr = langblock + 4; /* first 4 bytes are lang id and lang len */
97
  const char *NOTFOUND = "NLS_STRING_NOT_FOUND";
98
  /* find the string id in langblock memory */
99
  for (;;) {
100
    if (((unsigned short *)ptr)[0] == id) {
101
      ptr += 3;
102
      break;
103
    }
104
    if (ptr[2] == 0) {
105
      ptr = NOTFOUND;
106
      break;
107
    }
108
    ptr += ptr[2] + 3;
109
  }
110
  output_internal(ptr, nl);
111
}
112
 
113
 
388 mateuszvis 114
/* find first matching files using a FindFirst DOS call
115
 * returns 0 on success or a DOS err code on failure */
116
unsigned short findfirst(struct DTA *dta, const char *pattern, unsigned short attr) {
117
  unsigned short res = 0;
118
  _asm {
119
    /* set DTA location */
120
    mov ah, 0x1a
121
    mov dx, dta
122
    int 0x21
123
    /* */
124
    mov ah, 0x4e    /* FindFirst */
125
    mov dx, pattern
126
    mov cx, attr
127
    int 0x21        /* CF set on error + err code in AX, DTA filled with FileInfoRec on success */
128
    jnc DONE
129
    mov [res], ax
130
    DONE:
131
  }
132
  return(res);
133
}
134
 
135
 
136
/* find next matching, ie. continues an action intiated by findfirst() */
137
unsigned short findnext(struct DTA *dta) {
138
  unsigned short res = 0;
139
  _asm {
140
    mov ah, 0x4f    /* FindNext */
141
    mov dx, dta
142
    int 0x21        /* CF set on error + err code in AX, DTA filled with FileInfoRec on success */
143
    jnc DONE
144
    mov [res], ax
145
    DONE:
146
  }
147
  return(res);
148
}
392 mateuszvis 149
 
150
 
151
/* print s string and wait for a single key press from stdin. accepts only
152
 * key presses defined in the c ASCIIZ string. returns offset of pressed key
153
 * in string. keys in c MUST BE UPPERCASE! */
154
unsigned short askchoice(const char *s, const char *c) {
155
  unsigned short res;
156
  char key = 0;
157
 
158
  AGAIN:
159
  output(s);
160
  output(" ");
161
 
162
  _asm {
163
    push ax
164
    push dx
165
 
166
    mov ax, 0x0c01 /* clear input buffer and execute getchar (INT 21h,AH=1) */
167
    int 0x21
168
    /* if AL == 0 then this is an extended character */
169
    test al, al
170
    jnz GOTCHAR
171
    mov ah, 0x08   /* read again to flush extended char from input buffer */
172
    int 0x21
173
    xor al, al     /* all extended chars are ignored */
174
    GOTCHAR:       /* received key is in AL now */
175
    mov [key], al  /* save key */
176
 
177
    /* print a cr/lf */
178
    mov ah, 0x02
179
    mov dl, 0x0D
180
    int 0x21
181
    mov dl, 0x0A
182
    int 0x21
183
 
184
    pop dx
185
    pop ax
186
  }
187
 
188
  /* ucase() result */
189
  if ((key >= 'a') && (key <= 'z')) key -= ('a' - 'A');
190
 
191
  /* is there a match? */
192
  for (res = 0; c[res] != 0; res++) if (c[res] == key) return(res);
193
 
194
  goto AGAIN;
195
}
196
 
197
 
399 mateuszvis 198
/* converts a path to its canonic representation, returns 0 on success
199
 * or DOS err on failure (invalid drive) */
200
unsigned short file_truename(const char *src, char *dst) {
201
  unsigned short res = 0;
392 mateuszvis 202
  _asm {
399 mateuszvis 203
    push es
392 mateuszvis 204
    mov ah, 0x60  /* query truename, DS:SI=src, ES:DI=dst */
205
    push ds
206
    pop es
207
    mov si, src
208
    mov di, dst
209
    int 0x21
399 mateuszvis 210
    jnc DONE
211
    mov [res], ax
212
    DONE:
213
    pop es
392 mateuszvis 214
  }
399 mateuszvis 215
  return(res);
392 mateuszvis 216
}
217
 
218
 
219
/* returns DOS attributes of file, or -1 on error */
220
int file_getattr(const char *fname) {
221
  int res = -1;
222
  _asm {
223
    mov ax, 0x4300  /* query file attributes, fname at DS:DX */
224
    mov dx, fname
225
    int 0x21        /* CX=attributes if CF=0, otherwise AX=errno */
226
    jc DONE
227
    mov [res], cx
228
    DONE:
229
  }
230
  return(res);
231
}
396 mateuszvis 232
 
233
 
234
/* returns screen's width (in columns) */
235
unsigned short screen_getwidth(void) {
236
  /* BIOS 0040:004A = word containing screen width in text columns */
237
  unsigned short far *scrw = MK_FP(0x40, 0x4a);
238
  return(*scrw);
239
}
240
 
241
 
242
/* returns screen's height (in rows) */
243
unsigned short screen_getheight(void) {
244
  /* BIOS 0040:0084 = byte containing maximum valid row value (EGA ONLY) */
245
  unsigned char far *scrh = MK_FP(0x40, 0x84);
246
  if (*scrh == 0) return(25);  /* pre-EGA adapter */
247
  return(*scrh + 1);
248
}
249
 
250
 
251
/* displays the "Press any key to continue" msg and waits for a keypress */
252
void press_any_key(void) {
437 mateuszvis 253
  nls_output(15, 1); /* Press any key to continue... */
396 mateuszvis 254
  _asm {
255
    mov ah, 0x08  /* no echo console input */
256
    int 0x21      /* pressed key in AL now (0 for extended keys) */
257
    test al, al
258
    jnz DONE
259
    int 0x21      /* executed ah=8 again to read the rest of extended key */
260
    DONE:
261
    /* output CR/LF */
262
    mov ah, 0x02
263
    mov dl, 0x0D
264
    int 0x21
265
    mov dl, 0x0A
266
    int 0x21
267
  }
268
}
399 mateuszvis 269
 
270
 
271
/* validate a drive (A=0, B=1, etc). returns 1 if valid, 0 otherwise */
272
int isdrivevalid(unsigned char drv) {
273
  _asm {
274
    mov ah, 0x19  /* query default (current) disk */
275
    int 0x21      /* drive in AL (0=A, 1=B, etc) */
276
    mov ch, al    /* save current drive to ch */
277
    /* try setting up the drive as current */
278
    mov ah, 0x0E   /* select default drive */
279
    mov dl, [drv]  /* 0=A, 1=B, etc */
280
    int 0x21
281
    /* this call does not set CF on error, I must check cur drive to look for success */
282
    mov ah, 0x19  /* query default (current) disk */
283
    int 0x21      /* drive in AL (0=A, 1=B, etc) */
284
    mov [drv], 1  /* preset result as success */
285
    cmp al, dl    /* is eq? */
286
    je DONE
287
    mov [drv], 0  /* fail */
288
    jmp FAILED
289
    DONE:
290
    /* set current drive back to what it was initially */
291
    mov ah, 0x0E
292
    mov dl, ch
293
    int 0x21
294
    FAILED:
295
  }
296
  return(drv);
297
}
406 mateuszvis 298
 
299
 
300
/* converts a 8+3 filename into 11-bytes FCB format (MYFILE  EXT) */
301
void file_fname2fcb(char *dst, const char *src) {
302
  unsigned short i;
303
 
304
  /* fill dst with 11 spaces and a NULL terminator */
420 mateuszvis 305
  for (i = 0; i < 11; i++) dst[i] = ' ';
306
  dst[11] = 0;
406 mateuszvis 307
 
308
  /* copy fname until dot (.) or 8 characters */
309
  for (i = 0; i < 8; i++) {
310
    if ((src[i] == '.') || (src[i] == 0)) break;
311
    dst[i] = src[i];
312
  }
313
 
314
  /* advance src until extension or end of string */
315
  src += i;
316
  for (;;) {
317
    if (*src == '.') {
318
      src++; /* next character is extension */
319
      break;
320
    }
321
    if (*src == 0) break;
322
  }
323
 
324
  /* copy extension to dst (3 chars max) */
325
  dst += 8;
326
  for (i = 0; i < 3; i++) {
327
    if (src[i] == 0) break;
328
    dst[i] = src[i];
329
  }
330
}
331
 
332
 
333
/* converts a 11-bytes FCB filename (MYFILE  EXT) into 8+3 format (MYFILE.EXT) */
334
void file_fcb2fname(char *dst, const char *src) {
335
  unsigned short i, end = 0;
336
 
337
  for (i = 0; i < 8; i++) {
338
    dst[i] = src[i];
339
    if (dst[i] != ' ') end = i + 1;
340
  }
341
 
342
  /* is there an extension? */
343
  if (src[8] == ' ') {
344
    dst[end] = 0;
345
  } else { /* found extension: copy it until first space */
346
    dst[end++] = '.';
347
    for (i = 8; i < 11; i++) {
348
      if (src[i] == ' ') break;
349
      dst[end++] = src[i];
350
    }
351
    dst[end] = 0;
352
  }
353
}
410 mateuszvis 354
 
355
 
430 mateuszvis 356
/* converts an ASCIIZ string into an unsigned short. returns 0 on success.
357
 * on error, result will contain all valid digits that were read until
358
 * error occurred (0 on overflow or if parsing failed immediately) */
426 mateuszvis 359
int atous(unsigned short *r, const char *s) {
410 mateuszvis 360
  int err = 0;
361
 
362
  _asm {
363
    mov si, s
364
    xor ax, ax  /* general purpose register */
365
    xor cx, cx  /* contains the result */
366
    mov bx, 10  /* used as a multiplicative step */
367
 
368
    NEXTBYTE:
369
    xchg cx, ax /* move result into cx temporarily */
370
    lodsb  /* AL = DS:[SI++] */
371
    /* is AL 0? if so we're done */
372
    test al, al
373
    jz DONE
374
    /* validate that AL is in range '0'-'9' */
375
    sub al, '0'
430 mateuszvis 376
    jc FAIL   /* invalid character detected */
410 mateuszvis 377
    cmp al, 9
430 mateuszvis 378
    jg FAIL   /* invalid character detected */
410 mateuszvis 379
    /* restore result into AX (CX contains the new digit) */
380
    xchg cx, ax
381
    /* multiply result by 10 and add cl */
382
    mul bx    /* DX AX = AX * BX(10) */
430 mateuszvis 383
    jc OVERFLOW  /* overflow */
410 mateuszvis 384
    add ax, cx
430 mateuszvis 385
    /* if CF is set then overflow occurred (overflow part lands in DX) */
410 mateuszvis 386
    jnc NEXTBYTE
387
 
430 mateuszvis 388
    OVERFLOW:
389
    xor cx, cx  /* make sure result is zeroed in case overflow occured */
390
 
410 mateuszvis 391
    FAIL:
392
    inc [err]
393
 
394
    DONE: /* save result (CX) into indirect memory address r */
395
    mov bx, [r]
396
    mov [bx], cx
397
  }
398
  return(err);
399
}
415 mateuszvis 400
 
401
 
402
/* appends a backslash if path is a directory
403
 * returns the (possibly updated) length of path */
404
unsigned short path_appendbkslash_if_dir(char *path) {
405
  unsigned short len;
406
  int attr;
407
  for (len = 0; path[len] != 0; len++);
408
  if (len == 0) return(0);
409
  if (path[len - 1] == '\\') return(len);
410
  /* */
411
  attr = file_getattr(path);
412
  if ((attr > 0) && (attr & DOS_ATTR_DIR)) {
413
    path[len++] = '\\';
414
    path[len] = 0;
415
  }
416
  return(len);
417
}
416 mateuszvis 418
 
419
 
420
/* get current path drive d (A=1, B=2, etc - 0 is "current drive")
421
 * returns 0 on success, doserr otherwise */
422
unsigned short curpathfordrv(char *buff, unsigned char d) {
423
  unsigned short r = 0;
424
 
425
  _asm {
426
    /* is d == 0? then I need to resolve current drive */
427
    cmp byte ptr [d], 0
428
    jne GETCWD
429
    /* resolve cur drive */
430
    mov ah, 0x19  /* get current default drive */
431
    int 0x21      /* al = drive (00h = A:, 01h = B:, etc) */
432
    inc al        /* convert to 1=A, 2=B, etc */
433
    mov [d], al
434
 
435
    GETCWD:
436
    /* prepend buff with drive:\ */
437
    mov si, buff
438
    mov dl, [d]
439
    mov [si], dl
440
    add byte ptr [si], 'A' - 1
441
    inc si
442
    mov [si], ':'
443
    inc si
444
    mov [si], '\\'
445
    inc si
446
 
447
    mov ah, 0x47      /* get current directory of drv DL into DS:SI */
448
    int 0x21
449
    jnc DONE
450
    mov [r], ax       /* copy result from ax */
451
 
452
    DONE:
453
  }
454
 
455
  return(r);
456
}
420 mateuszvis 457
 
458
 
459
/* fills a nls_patterns struct with current NLS patterns, returns 0 on success, DOS errcode otherwise */
460
unsigned short nls_getpatterns(struct nls_patterns *p) {
461
  unsigned short r = 0;
462
 
463
  _asm {
464
    mov ax, 0x3800  /* DOS 2+ -- Get Country Info for current country */
465
    mov dx, p       /* DS:DX points to the CountryInfoRec buffer */
466
    int 0x21
467
    jnc DONE
468
    mov [r], ax     /* copy DOS err code to r */
469
    DONE:
470
  }
471
 
472
  return(r);
473
}
474
 
475
 
476
/* computes a formatted date based on NLS patterns found in p
477
 * returns length of result */
478
unsigned short nls_format_date(char *s, unsigned short yr, unsigned char mo, unsigned char dy, const struct nls_patterns *p) {
479
  unsigned short items[3];
480
  /* preset date/month/year in proper order depending on date format */
481
  switch (p->dateformat) {
482
    case 0:  /* USA style: m d y */
483
      items[0] = mo;
484
      items[1] = dy;
485
      items[2] = yr;
486
      break;
487
    case 1:  /* EU style: d m y */
488
      items[0] = dy;
489
      items[1] = mo;
490
      items[2] = yr;
491
      break;
492
    case 2:  /* Japan-style: y m d */
493
    default:
494
      items[0] = yr;
495
      items[1] = mo;
496
      items[2] = dy;
497
      break;
498
  }
499
  /* compute the string */
500
  return(sprintf(s, "%02u%s%02u%s%02u", items[0], p->datesep, items[1], p->datesep, items[2]));
501
}
502
 
503
 
426 mateuszvis 504
/* computes a formatted time based on NLS patterns found in p, sc are ignored if set 0xff
420 mateuszvis 505
 * returns length of result */
426 mateuszvis 506
unsigned short nls_format_time(char *s, unsigned char ho, unsigned char mn, unsigned char sc, const struct nls_patterns *p) {
507
  char ampm = 0;
508
  unsigned short res;
509
 
420 mateuszvis 510
  if (p->timefmt == 0) {
511
    if (ho == 12) {
426 mateuszvis 512
      ampm = 'p';
420 mateuszvis 513
    } else if (ho > 12) {
514
      ho -= 12;
426 mateuszvis 515
      ampm = 'p';
420 mateuszvis 516
    } else { /* ho < 12 */
517
      if (ho == 0) ho = 12;
426 mateuszvis 518
      ampm = 'a';
420 mateuszvis 519
    }
426 mateuszvis 520
    res = sprintf(s, "%2u", ho);
521
  } else {
522
    res = sprintf(s, "%02u", ho);
420 mateuszvis 523
  }
426 mateuszvis 524
 
525
  /* append separator and minutes */
526
  res += sprintf(s + res, "%s%02u", p->timesep, mn);
527
 
528
  /* if seconds provided, append them, too */
529
  if (sc != 0xff) res += sprintf(s + res, "%s%02u", p->timesep, sc);
530
 
531
  /* finally append AM/PM char */
532
  if (ampm != 0) s[res++] = ampm;
533
  s[res] = 0;
534
 
535
  return(res);
420 mateuszvis 536
}
537
 
538
 
539
/* computes a formatted integer number based on NLS patterns found in p
540
 * returns length of result */
423 mateuszvis 541
unsigned short nls_format_number(char *s, unsigned long num, const struct nls_patterns *p) {
542
  unsigned short sl = 0, i;
420 mateuszvis 543
  unsigned char thcount = 0;
544
 
423 mateuszvis 545
  /* write the value (reverse) with thousand separators (if any defined) */
420 mateuszvis 546
  do {
547
    if ((thcount == 3) && (p->thousep[0] != 0)) {
548
      s[sl++] = p->thousep[0];
549
      thcount = 0;
550
    }
551
    s[sl++] = '0' + num % 10;
552
    num /= 10;
553
    thcount++;
554
  } while (num > 0);
555
 
423 mateuszvis 556
  /* terminate the string */
420 mateuszvis 557
  s[sl] = 0;
558
 
423 mateuszvis 559
  /* reverse the string now (has been built in reverse) */
560
  for (i = sl / 2 + (sl & 1); i < sl; i++) {
420 mateuszvis 561
    thcount = s[i];
423 mateuszvis 562
    s[i] = s[sl - (i + 1)];   /* abc'de  if i=3 then ' <-> c */
420 mateuszvis 563
    s[sl - (i + 1)] = thcount;
564
  }
565
 
423 mateuszvis 566
  return(sl);
420 mateuszvis 567
}
437 mateuszvis 568
 
569
 
570
/* reload nls ressources from svarcom.lng into langblock */
571
void nls_langreload(char *buff, unsigned short env) {
572
  unsigned short i;
573
  const char far *nlspath;
574
  char *langblockptr = langblock;
575
  unsigned short lang;
576
  unsigned short errcode = 0;
577
 
578
  /* look up the LANG env variable, upcase it and copy to lang */
579
  nlspath = env_lookup_val(env, "LANG");
580
  if ((nlspath == NULL) || (nlspath[0] == 0)) return;
581
  buff[0] = nlspath[0];
582
  buff[1] = nlspath[1];
583
  buff[2] = 0;
584
 
585
  if (buff[0] >= 'a') buff[0] -= 'a' - 'A';
586
  if (buff[1] >= 'a') buff[1] -= 'a' - 'A';
587
  memcpy(&lang, buff, 2);
588
 
589
  /* check if there is need to reload at all */
590
  if (((unsigned short *)langblock)[0] == lang) return;
591
 
592
  /* printf("NLS RELOAD (curlang=%04X ; toload=%04X\r\n", ((unsigned short *)langblock)[0], lang); */
593
 
594
  nlspath = env_lookup_val(env, "NLSPATH");
595
  if ((nlspath == NULL) || (nlspath[0] == 0)) return;
596
 
597
  /* copy NLSPATH(far) to buff */
598
  for (i = 0; nlspath[i] != 0; i++) buff[i] = nlspath[i];
599
 
600
  /* terminate with a bkslash, if not already the case */
601
  if (buff[i - 1] != '\\') buff[i++] = '\\';
602
 
603
  /* append "svarcom.lng" */
604
  strcpy(buff + i, "SVARCOM.LNG");
605
 
606
  /* copy file content to langblock */
607
  _asm {
608
    push ax
609
    push bx
610
    push cx
611
    push dx
612
    push si
613
    push di
614
 
615
    /* make sure ES=DS and clear DF (will be useful for string matching) */
616
    push ds
617
    pop es
618
    cld
619
 
620
    /* preset SI to buff */
621
    mov si, buff
622
 
623
    /* Open File */
624
    mov bx, 0xffff /* bx holds the file handle (0xffff = not set) */
625
    mov ax, 0x3d00 /* DOS 2+ -- Open File for read */
626
    mov dx, si     /* fname */
627
    int 0x21       /* cf set on error, otherwise handle in AX */
628
    jnc OPENOK
629
    jmp FAIL
630
    OPENOK:
631
    mov bx, ax    /* save file handle to bx */
632
 
633
    /* read hdr */
634
    mov ah, 0x3f   /* DOS 2+ -- Read from File via Handle in BX */
635
    mov cx, 4      /* read 4 bytes */
636
    mov dx, si
637
    int 0x21
638
    jnc READHDROK
639
    jmp FAIL
640
 
641
    READHDROK:
642
 
643
    cmp ax, cx  /* hdr must be 4 bytes long (SvL\x1b) */
644
    jne FAIL
645
    /* check that sig is Svl\x1b */
646
    mov di, si
647
    cld         /* scasw must inc DI */
648
    mov ax, 'vS'
649
    scasw       /* cmp ax, ES:[DI] and DI += 2*/
650
    jne FAIL
651
    mov ax, 0x1B4C
652
    scasw       /* cmp ax, ES:[DI] and DI += 2*/
653
    jne FAIL
654
 
655
    READLANGID:
656
    /* read lang id */
657
    mov ah, 0x3f   /* Read from File via Handle in BX */
658
    /* mov bx, [i]  already set */
659
    mov cx, 4
660
    mov dx, si
661
    int 0x21
662
    jc FAIL
663
    cmp ax, cx
664
    jne FAIL
665
    /* is this the LANG I am looking for? */
666
    mov ax, [lang]
667
    mov di, si
668
    scasw       /* cmp ax, ES:[DI] and DI += 2*/
669
    je LOADSTRINGS
670
    /* skip to next lang */
671
    mov ax, 0x4201   /* move file pointer CX:DX bytes forward */
672
    /* mov bx, [i]  file handle */
673
    xor cx, cx
674
    mov dx, [di]
675
    int 0x21
676
    jc FAIL
677
    jmp READLANGID
678
 
679
    LOADSTRINGS:
680
 
681
    /* copy langid and langlen to langblockptr */
682
    mov di, langblockptr
683
    mov ax, [si]
684
    stosw   /* mov [di], ax and di += 2 */
685
    mov ax, [si+2]
686
    stosw
687
    /* read strings (buff+2 bytes) into langblock */
688
    mov ah, 0x3f   /* Read from File via Handle in BX */
689
    mov cx, [si+2]
690
    mov dx, di
691
    int 0x21
692
    jnc DONE
693
 
694
    /* on error make sure to zero out langblock's header */
695
    xor cx, cx
696
    mov [di], cx                 /* langblock id*/
697
    mov [di + 2], cx /* langblock len */
698
    mov [di + 4], cx /* 1st string id */
699
    mov [di + 6], cx /* 1st string len */
700
 
701
    /* cleanup and quit */
702
    FAIL:
703
    mov [errcode], ax
704
    DONE:
705
    /* close file handle if set */
706
    cmp bx, 0xffff
707
    je FNOTOPEN
708
    mov ah, 0x3e  /* DOS 2+ -- Close a File Handle (Handle in BX) */
709
    int 0x21
710
    FNOTOPEN:
711
 
712
    pop di
713
    pop si
714
    pop dx
715
    pop cx
716
    pop bx
717
    pop ax
718
  }
719
 
720
  if (errcode != 0) printf("AX=%04x\r\n", errcode);
721
}