chmbase.pas 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. { Copyright (C) <2005> <Andrew Haines> chmbase.pas
  2. This library is free software; you can redistribute it and/or modify it
  3. under the terms of the GNU Library General Public License as published by
  4. the Free Software Foundation; either version 2 of the License, or (at your
  5. option) any later version.
  6. This program is distributed in the hope that it will be useful, but WITHOUT
  7. ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  8. FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
  9. for more details.
  10. You should have received a copy of the GNU Library General Public License
  11. along with this library; if not, write to the Free Software Foundation,
  12. Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  13. }
  14. {
  15. See the file COPYING.FPC, included in this distribution,
  16. for details about the copyright.
  17. }
  18. unit chmbase;
  19. {$mode objfpc}{$H+}
  20. interface
  21. uses
  22. Classes, SysUtils;
  23. type
  24. {$PACKRECORDS C}
  25. TITSFHeader= record
  26. ITSFsig: array [0..3] of char;
  27. Version: LongWord;
  28. HeaderLength: LongWord;
  29. Unknown_1: LongWord;
  30. TimeStamp: LongWord; //bigendian
  31. LanguageID: LongWord;
  32. Guid1: TGuid;
  33. Guid2: TGuid;
  34. end;
  35. TITSFHeaderEntry = record
  36. PosFromZero: QWord;
  37. Length: QWord;
  38. end;
  39. //Version 3 has this qword. 2 does not
  40. TITSFHeaderSuffix = record
  41. Offset: QWord; // offset within file of content section 0
  42. end;
  43. TITSPHeaderPrefix = record
  44. Unknown1: LongWord;// = $01FE
  45. Unknown2: LongWord;// = 0
  46. FileSize: QWord;
  47. Unknown3: LongWord;// =0
  48. Unknown4: LongWord;// =0
  49. end;
  50. TITSPHeader = record
  51. ITSPsig: array [0..3] of char; // = 'ITSP'
  52. Version: LongWord; // =1
  53. DirHeaderLength: Longword; // Length of the directory header
  54. Unknown1: LongWord; // =$0a
  55. ChunkSize: LongWord; // $1000
  56. Density: LongWord; // usually = 2
  57. IndexTreeDepth: LongWord;// 1 if there is no index 2 if there is one level of PMGI chunks
  58. IndexOfRootChunk: LongInt;// -1 if no root chunk
  59. FirstPMGLChunkIndex,
  60. LastPMGLChunkIndex: LongWord;
  61. Unknown2: LongInt; // = -1
  62. DirectoryChunkCount: LongWord;
  63. LanguageID: LongWord;
  64. GUID: TGuid;
  65. LengthAgain: LongWord; //??? $54
  66. Unknown3: LongInt; // = -1
  67. Unknown4: LongInt; // = -1
  68. Unknown5: LongInt; // = -1
  69. end;
  70. TPMGchunktype = (ctPMGL, ctPMGI, ctUnknown);
  71. TPMGListChunk = record
  72. PMGLsig: array [0..3] of char;
  73. UnusedSpace: Longword; ///!!! this value can also represent the size of quickref area in the end of the chunk
  74. Unknown1: Longword; //always 0
  75. PreviousChunkIndex: LongInt; // chunk number of the prev listing chunk when reading dir in sequence
  76. // (-1 if this is the first listing chunk)
  77. NextChunkIndex: LongInt; // chunk number of the next listing chunk (-1 if this is the last chunk)
  78. end;
  79. PPMGListChunkEntry = ^TPMGListChunkEntry;
  80. TPMGListChunkEntry = record
  81. //NameLength: LongInt; we don't need this permanantly so I've moved it to a temp var
  82. Name: String;
  83. ContentSection: LongWord;//QWord;
  84. ContentOffset: QWord;
  85. DecompressedLength: QWord;
  86. end;
  87. TPMGIIndexChunk = record
  88. PMGIsig: array [0..3] of char;
  89. UnusedSpace: LongWord; // has a quickref area
  90. end;
  91. TPMGIIndexChunkEntry = record
  92. Name: String;
  93. ListingChunk: DWord;
  94. end;
  95. const
  96. ITSFHeaderGUID : TGuid = '{7C01FD10-7BAA-11D0-9E0C-00A0C922E6EC}';
  97. ITSFFileSig: array [0..3] of char = 'ITSF';
  98. ITSPHeaderGUID : TGuid = '{5D02926A-212E-11D0-9DF9-00A0C922E6EC}';
  99. ITSPHeaderSig: array [0..3] of char = 'ITSP';
  100. // this function will advance the stream to the end of the compressed integer
  101. // and return the value
  102. function GetCompressedInteger(const Stream: TStream): DWord;
  103. // returns the number of bytes written to the stream
  104. function WriteCompressedInteger(const Stream: TStream; ANumber: DWord): DWord;
  105. function WriteCompressedInteger(Buffer: Pointer; ANumber: DWord): DWord;
  106. // stupid needed function
  107. function ChmCompareText(S1, S2: String): Integer; inline;
  108. implementation
  109. function GetCompressedInteger(const Stream: TStream): DWord;
  110. var
  111. total: QWord = 0;
  112. temp: Byte;
  113. Sanity: Integer = 0;
  114. begin
  115. try
  116. temp := Stream.ReadByte;
  117. while temp >= $80 do begin
  118. total := total shl 7;
  119. total := total + temp and $7f;
  120. temp := Stream.ReadByte;
  121. Inc(Sanity);
  122. if Sanity > 8 then begin
  123. Result := 0;
  124. Exit;
  125. end;
  126. end;
  127. Result := (total shl 7) + temp;
  128. except
  129. Result := 0;
  130. end;
  131. end;
  132. // returns how many bytes were written
  133. function WriteCompressedInteger(const Stream: TStream; ANumber: DWord): DWord;
  134. var
  135. Buffer: QWord; // Easily large enough
  136. begin
  137. Result := WriteCompressedInteger(@Buffer, ANumber);
  138. Result := Stream.Write(Buffer, Result);
  139. end;
  140. // returns how many bytes were written
  141. function WriteCompressedInteger(Buffer: Pointer; ANumber: DWord): DWord;
  142. var
  143. bit: dword;
  144. mask: QWord;
  145. buf: PByte;
  146. Value: QWord = 0;
  147. TheEnd: DWord = 0;
  148. begin
  149. bit := (sizeof(dWord)*8)div 7*7;
  150. buf := @Value;
  151. while True do begin
  152. mask := $7f shl bit;
  153. if (bit = 0) or ((ANumber and mask)<>0) then break;
  154. Dec(bit, 7);
  155. end;
  156. while True do begin
  157. buf^ := Byte(((ANumber shr bit)and $7f));
  158. if(bit = 0) then break;
  159. buf^ := buf^ or $80;
  160. Inc(buf);
  161. Dec(bit, 7);
  162. Inc(TheEnd);
  163. end;
  164. buf := @Value;
  165. Result := TheEnd+1;
  166. Move(Value, Buffer^, Result);
  167. if Result > 8 then WriteLn(' ', ANumber,' WRITE_COMPRESSED_INTEGER too big!: ', Result, ' ');
  168. end;
  169. function ChmCompareText(S1, S2: String): Integer; inline;
  170. begin
  171. // for our purposes the CompareText function will not work.
  172. Result := CompareStr(LowerCase(S1), Lowercase(S2));
  173. end;
  174. end.