Subversion Repositories autosfx

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 daniel-mar 1
unit ZMMsgStr19;
2
 
3
(*
4
  ZMMsgStr19.pas - message string handler
5
    Copyright (C) 2009, 2010  by Russell J. Peters, Roger Aelbrecht,
6
      Eric W. Engler and Chris Vleghert.
7
 
8
        This file is part of TZipMaster Version 1.9.
9
 
10
    TZipMaster is free software: you can redistribute it and/or modify
11
    it under the terms of the GNU Lesser General Public License as published by
12
    the Free Software Foundation, either version 3 of the License, or
13
    (at your option) any later version.
14
 
15
    TZipMaster is distributed in the hope that it will be useful,
16
    but WITHOUT ANY WARRANTY; without even the implied warranty of
17
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
    GNU Lesser General Public License for more details.
19
 
20
    You should have received a copy of the GNU Lesser General Public License
21
    along with TZipMaster.  If not, see <http://www.gnu.org/licenses/>.
22
 
23
    contact: problems@delphizip.org (include ZipMaster in the subject).
24
    updates: http://www.delphizip.org
25
    DelphiZip maillist subscribe at http://www.freelists.org/list/delphizip
26
 
27
  modified 2010-03-15
28
  --------------------------------------------------------------------------- *)
29
 
30
interface
31
 
32
uses
33
  Classes, ZipMstr19, ZMCompat19;
34
 
35
 
36
var
37
  OnZMStr: TZMLoadStrEvent;
38
 
39
function LoadZipStr(id: Integer): String;
40
 
41
// '*' = Auto, '' = default US,  language  (number = language id)
42
function SetZipMsgLanguage(const zl: String): String;
43
// get language at index (<0 - default, 0 - current)
44
function GetZipMsgLanguage(idx: Integer): String;
45
// info (-1) = language id, 0 = name, other values as per windows LOCALE_
46
function GetZipMsgLanguageInfo(idx: Integer; info: Cardinal): String;
47
 
48
function LanguageIdent(const seg: Ansistring): Ansistring;
49
function LocaleInfo(loc: Integer; info: Cardinal): String;
50
 
51
implementation
52
 
53
uses
54
  Windows, SysUtils,
55
{$IFDEF UNICODE}
56
  AnsiStrings,
57
{$ELSE}
58
  ZMUTF819,
59
{$ENDIF}
60
  ZMUtils19, ZMMsg19, ZMDefMsgs19, ZMExtrLZ7719;
61
 
62
{$I '.\ZMConfig19.inc'}
63
 
64
{$IFNDEF VERD6up}
65
type
66
  PCardinal = ^Cardinal;
67
{$ENDIF}
68
 
69
const
70
  SResourceMissingFor = 'Resource missing for ';
71
  SUSDefault = 'US: default';
72
  lchars = ['A' .. 'Z', 'a' .. 'z', '0' .. '9', '_'];
73
  Uchars = ['a' .. 'z'];
74
  Digits = ['0' .. '9'];
75
 
76
var
77
{$IFDEF USE_COMPRESSED_STRINGS}
78
  DefRes: TZMRawBytes; // default strings
79
{$ENDIF}
80
  SelRes: TZMRawBytes; // selected strings
81
  SelId: Cardinal;
82
  SelName: Ansistring;
83
  Setting: bool;
84
 
85
function LanguageIdent(const seg: Ansistring): Ansistring;
86
var
87
  c: AnsiChar;
88
  i: Integer;
89
begin
90
  Result := '';
91
  for i := 1 to length(seg) do
92
  begin
93
    c := seg[i];
94
    if not(c in lchars) then
95
    begin
96
      if (Result <> '') or (c <> ' ') then
97
        break;
98
    end
99
    else
100
    begin
101
      if (Result = '') and (c in Digits) then
102
        break; // must not start with digit
103
      if c in Uchars then
104
        c := AnsiChar(Ord(c) - $20); // convert to upper
105
      Result := Result + c;
106
    end;
107
  end;
108
  // Result := Uppercase(Result);
109
end;
110
 
111
// format is
112
// id: word, data_size: word, label_size: word, label: char[], data: byte[];... ;0
113
// stream at id
114
// returns id
115
function LoadFromStream(var blk: TZMRawBytes; src: TStream): Cardinal;
116
var
117
  r: Integer;
118
  so: TMemoryStream;
119
  sz: array [0 .. 2] of Word;
120
  szw: Integer;
121
begin
122
  blk := ''; // empty it
123
  Result := 0;
124
  if src.Read(sz[0], 3 * sizeof(Word)) <> (3 * sizeof(Word)) then
125
    exit;
126
  if src.Size < (sz[1] + sz[2] + (3 * sizeof(Word))) then
127
    exit;
128
  src.Position := src.Position + sz[2]; // skip name
129
  try
130
    so := TMemoryStream.Create;
131
    r := LZ77Extract(so, src, sz[1]);
132
    // Assert(r = 0, 'error extracting strings');
133
    if (r = 0) and (so.Size < 50000) then
134
    begin
135
      szw := (Integer(so.Size) + (sizeof(Word) - 1));
136
      SetLength(blk, szw + sizeof(Word));
137
      so.Position := 0;
138
      if so.Read(blk[1], so.Size) = so.Size then
139
      begin
140
        blk[szw + 1] := #255;
141
        blk[szw + 2] := #255;
142
        Result := sz[0];
143
      end
144
      else
145
        blk := '';
146
    end;
147
  finally
148
    FreeAndNil(so);
149
  end;
150
end;
151
 
152
// format is
153
// id: word, data_size: word, label_size: word, label: char[], data: byte[];... ;0
154
// positions stream to point to id
155
// SegName has identifier terminated by ':'
156
function FindInStream(src: TStream; var SegName: Ansistring; var LangId: Word)
157
  : Boolean;
158
var
159
  c: AnsiChar;
160
  i: Word;
161
  p: Int64;
162
  s: Ansistring;
163
  seg: Ansistring;
164
  ss: Int64;
165
  uname: Ansistring;
166
  w: Word;
167
  w3: array [0 .. 2] of Word;
168
begin
169
  Result := False;
170
  if not assigned(src) then
171
    exit;
172
  seg := LanguageIdent(SegName);
173
  if (length(seg) < 2) and (LangId = 0) then
174
    exit;
175
  uname := '';
176
  for i := 1 to length(SegName) do
177
  begin
178
    c := SegName[i];
179
    if c in Uchars then
180
      c := AnsiChar(Ord(c) - $20);
181
    uname := uname + c;
182
  end;
183
  p := src.Position;
184
  ss := src.Size - ((3 * sizeof(Word)) + 2); // id + dlen + nlen + min 2 chars
185
  while (not Result) and (p < ss) do
186
  begin
187
    src.Position := p;
188
    src.ReadBuffer(w3[0], 3 * sizeof(Word)); // id, dsize, nsize
189
    w := w3[2]; // name size
190
    if w > 0 then
191
    begin
192
      SetLength(s, w);
193
      src.ReadBuffer(s[1], w); // read name
194
    end;
195
    if LangId = 0 then
196
    begin
197
      // find by name
198
      Result := False;
199
      for i := 1 to w do
200
      begin
201
        c := s[i];
202
        if not(c in lchars) then
203
          break;
204
        if i > length(uname) then
205
        begin
206
          Result := False;
207
          break;
208
        end;
209
        if c in Uchars then
210
          c := AnsiChar(Ord(c) - $20);
211
        Result := c = uname[i];
212
        if not Result then
213
          break;
214
      end;
215
    end
216
    else // find by language ID
217
      Result := (LangId = w3[0]) or
218
        ((LangId < $400) and ((w3[0] and $3FF) = LangId));
219
    if not Result then
220
      p := src.Position + w3[1]; // skip data to next entry
221
  end;
222
  if Result then
223
  begin
224
    SegName := s;
225
    LangId := w3[0];
226
    src.Position := p;
227
  end;
228
end;
229
 
230
// format is
231
// id: word, data_size: word, label_size: word, label: char[], data: byte[];... ;0
232
// positions stream to point to id
233
// segname has identifier terminated by ':'
234
function IdInStream(src: TStream; var idx: Cardinal; var lang: String): Boolean;
235
var
236
  p: Int64;
237
  s: Ansistring;
238
  ss: Int64;
239
  w3: array [0 .. 2] of Word;
240
begin
241
  Result := False;
242
  if (idx < 1) or not assigned(src) then
243
    exit;
244
  p := src.Position;
245
  ss := src.Size - ((3 * sizeof(Word)) + 20); // id + dlen + nlen + 20 bytes
246
  if p > ss then
247
    exit;
248
  repeat
249
    src.ReadBuffer(w3[0], 3 * sizeof(Word)); // id, dsize, nsize
250
    if idx <= 1 then
251
      break;
252
    Dec(idx);
253
    p := src.Position + w3[1] + w3[2]; // after name + data
254
    if p < ss then
255
      src.Position := p
256
    else
257
      exit;
258
  until False;
259
  SetLength(s, w3[2]);
260
  src.ReadBuffer(s[1], w3[2]); // read name
261
  lang := String(s);
262
  idx := w3[0];
263
  src.Position := p;
264
  Result := True;
265
end;
266
 
267
{$IFNDEF VERD6up}
268
function TryStrToInt(const s: String; var v: Integer): Boolean;
269
begin
270
  if (s = '') or not CharInSet(s[1], ['0' .. '9', '$']) then
271
    Result := False
272
  else
273
  begin
274
    Result := True;
275
    try
276
      v := StrToInt(s);
277
    except
278
      on EConvertError do
279
        Result := False;
280
    end;
281
  end;
282
end;
283
{$ENDIF}
284
 
285
function SetZipMsgLanguage(const zl: String): String;
286
var
287
  i: Integer;
288
  id: Word;
289
  len: Integer;
290
  LangName: Ansistring;
291
  newBlock: TZMRawBytes;
292
  newId: Cardinal;
293
  newres: TZMRawBytes;
294
  res: TResourceStream;
295
begin
296
  if (zl = '') or Setting then
297
    exit;
298
  res := nil;
299
  try
300
    Setting := True;
301
    SelRes := ''; // reset to default
302
    SelId := 0;
303
    SelName := '';
304
    Result := '';
305
    id := 0;
306
    LangName := LanguageIdent(Ansistring(zl));
307
    if (length(LangName) < 2) then
308
    begin
309
      if zl = '*' then
310
        id := GetUserDefaultLCID
311
      else
312
      begin
313
        if (not TryStrToInt(zl, i)) or (i <= 0) or (i > $0FFFF) then
314
          exit;
315
        id := Cardinal(i);
316
      end;
317
    end;
318
    if (LangName <> 'US') and (id <> $0409) then // use default US
319
    begin
320
      res := OpenResStream(DZRES_Str, RT_RCData);
321
      if assigned(res) and FindInStream(res, LangName, id) then
322
      begin
323
        newId := LoadFromStream(newBlock, res);
324
        if newId > 0 then
325
        begin
326
          len := length(newBlock);
327
          SetLength(newres, len);
328
          Move(newBlock[1], PAnsiChar(newres)^, len);
329
          Result := String(LangName);
330
          SelRes := newres;
331
          SelName := LangName;
332
          SelId := newId;
333
        end;
334
      end;
335
    end;
336
  finally
337
    Setting := False;
338
    FreeAndNil(res);
339
  end;
340
end;
341
 
342
function LocaleInfo(loc: Integer; info: Cardinal): String;
343
var
344
  s: String;
345
begin
346
  if (loc <= 0) or (loc = $400) then
347
    loc := LOCALE_USER_DEFAULT;
348
  SetLength(s, 1024);
349
  GetLocaleInfo(loc and $FFFF, info, PChar(s), 1023);
350
  Result := PChar(s); // remove any trailing #0
351
end;
352
 
353
// get language at Idx (<0 - default, 0 - current)
354
// info (-1) = language id, 0 = name, other values as per windows LOCALE_
355
function GetZipMsgLanguageInfo(idx: Integer; info: Cardinal): String;
356
var
357
  id: Cardinal;
358
  res: TResourceStream;
359
  s: String;
360
begin
361
  id := $0409;
362
  Result := SUSDefault; // default US English
363
  if (idx = 0) and (SelRes <> '') then
364
  begin
365
    Result := String(SelName);
366
    id := SelId;
367
  end;
368
  if idx > 0 then
369
  begin
370
    res := nil;
371
    Result := '><';
372
    id := idx and $FF;
373
    try
374
      res := OpenResStream(DZRES_Str, RT_RCData);
375
      if assigned(res) and IdInStream(res, id, s) then
376
        Result := s;
377
    finally
378
      FreeAndNil(res);
379
    end;
380
  end;
381
  if Result <> '><' then
382
  begin
383
    if info = 0 then
384
      Result := '$' + IntToHex(id, 4)
385
    else if info <> Cardinal(-1) then
386
      Result := LocaleInfo(id, info);
387
  end;
388
end;
389
 
390
// get language at index (<0 - current, 0 - default, >0 - index)
391
function GetZipMsgLanguage(idx: Integer): String;
392
begin
393
  Result := GetZipMsgLanguageInfo(idx, Cardinal(-1));
394
end;
395
 
396
// Delphi does not like adding offset
397
function _ofsp(blk: PWord; ofs: Integer): PWord;
398
begin
399
  Result := blk;
400
  inc(Result, ofs);
401
end;
402
 
403
// returns String
404
function FindRes1(blkstr: TZMRawBytes; id: Integer): String;
405
var
406
  blkP: PWord;
407
  bp: Integer;
408
  DatSiz: Cardinal;
409
  fid: Integer;
410
  HedSiz: Cardinal;
411
  hp: Integer;
412
  l: Cardinal;
413
  mx: Integer;
414
  rid: Integer;
415
  sz: Cardinal;
416
  ws: WideString;
417
begin
418
  Result := '';
419
  if blkstr = '' then
420
    exit;
421
  fid := id div 16;
422
  try
423
    blkP := PWord(PAnsiChar(blkstr));
424
    bp := 0;
425
    mx := length(blkstr) div sizeof(Word);
426
    while (bp + 9) < mx do
427
    begin
428
      bp := (bp + 1) and $7FFFE; // dword align
429
      DatSiz := pCardinal(_ofsp(blkP, bp))^;
430
      HedSiz := pCardinal(_ofsp(blkP, bp + 2))^;
431
      if (HedSiz + DatSiz) < 8 then
432
        break;
433
      // Assert((HedSiz + DatSiz) >= 8, 'header error');
434
      sz := (HedSiz + DatSiz) - 8;
435
      hp := bp + 4;
436
      inc(bp, 4 + (sz div 2));
437
      if _ofsp(blkP, hp)^ <> $FFFF then
438
        continue; // bad res type
439
      if _ofsp(blkP, hp + 1)^ <> 6 then
440
        continue; // not string table
441
      if _ofsp(blkP, hp + 2)^ <> $FFFF then
442
        continue;
443
      rid := pred(_ofsp(blkP, hp + 3)^);
444
      if fid <> rid then
445
        continue;
446
      rid := rid * 16;
447
      inc(hp, (HedSiz - 8) div 2);
448
      ws := '';
449
      while rid < id do
450
      begin
451
        l := _ofsp(blkP, hp)^;
452
        inc(hp, l + 1);
453
        inc(rid);
454
      end;
455
      l := _ofsp(blkP, hp)^;
456
      if l <> 0 then
457
      begin
458
        SetLength(ws, l);
459
        Move(_ofsp(blkP, hp + 1)^, ws[1], l * sizeof(Widechar));
460
        Result := ws;
461
        Result := StringReplace(Result, #10, #13#10, [rfReplaceAll]);
462
        break;
463
      end;
464
      break;
465
    end;
466
  except
467
    Result := '';
468
  end;
469
end;
470
{$IFNDEF USE_COMPRESSED_STRINGS}
471
 
472
function FindConst(id: Integer): String;
473
var
474
  p: pResStringRec;
475
 
476
  function Find(idx: Integer): pResStringRec;
477
  var
478
    wi: Word;
479
    i: Integer;
480
  begin
481
    Result := nil;
482
    wi := Word(idx);
483
    for i := 0 to high(ResTable) do
484
      if ResTable[i].i = wi then
485
      begin
486
        Result := ResTable[i].s;
487
        break;
488
      end;
489
  end;
490
 
491
begin { FindConst }
492
  Result := '';
493
  if id < 10000 then
494
    exit;
495
  p := Find(id);
496
  if p <> nil then
497
    Result := LoadResString(p);
498
end;
499
{$ELSE}
500
 
501
// format is
502
// id: word, data_size: word, label_size: word, label: char[], data: byte[];... ;0
503
function LoadCompressedDef(const src): Integer;
504
var
505
  ms: TMemoryStream;
506
  w: Word;
507
  pw: PWord;
508
begin
509
  Result := -1;
510
  pw := @src;
511
  if pw^ = $0409 then
512
  begin
513
    inc(pw);
514
    w := pw^;
515
    inc(pw);
516
    inc(w, pw^);
517
    inc(w, (3 * sizeof(Word)));
518
    try
519
      ms := TMemoryStream.Create;
520
      ms.Write(src, w);
521
      ms.Position := 0;
522
      Result := LoadFromStream(DefRes, ms);
523
    finally
524
      FreeAndNil(ms);
525
    end;
526
  end;
527
end;
528
{$ENDIF}
529
 
530
// returns String
531
function LoadZipStr(id: Integer): String;
532
var
533
  d: String;
534
  blk: TZMRawBytes;
535
  tmpOnZipStr: TZMLoadStrEvent;
536
begin
537
  Result := '';
538
  blk := SelRes;
539
  Result := FindRes1(blk, id);
540
{$IFDEF USE_COMPRESSED_STRINGS}
541
  if Result = '' then
542
  begin
543
    if DefRes = '' then
544
      LoadCompressedDef(CompBlok);
545
    blk := DefRes;
546
    Result := FindRes1(blk, id);
547
  end;
548
{$ELSE}
549
  if Result = '' then
550
    Result := FindConst(id);
551
{$ENDIF}
552
  tmpOnZipStr := OnZMStr;
553
  if assigned(tmpOnZipStr) then
554
  begin
555
    d := Result;
556
    tmpOnZipStr(id, d);
557
    if d <> '' then
558
      Result := d;
559
  end;
560
  if Result = '' then
561
    Result := SResourceMissingFor + IntToStr(id);
562
end;
563
 
564
initialization
565
  OnZMStr := nil;
566
  Setting := False;
567
{$IFDEF USE_COMPRESSED_STRINGS}
568
  DefRes := '';
569
{$ENDIF}
570
  SelRes := '';
571
 
572
finalization
573
{$IFDEF USE_COMPRESSED_STRINGS}
574
  DefRes := ''; // force destruction
575
{$ENDIF}
576
  SelRes := '';
577
 
578
end.