BB.StdCoder.Mod 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. MODULE StdCoder; (** AUTHOR "GF"; PURPOSE "Extract sources from BlackBox standardcoded archives" *)
  2. IMPORT Streams, Commands, Files, Strings, Log := KernelLog;
  3. CONST
  4. Tag = "StdCoder.Decode";
  5. Ok = Streams.Ok;
  6. OldVersion = 0; ThisVersion = 1;
  7. View = 1; File = 2; List = 3;
  8. N = 16384;
  9. TYPE
  10. FileType = ARRAY 16 OF CHAR;
  11. FileList = POINTER TO RECORD
  12. next: FileList;
  13. oberonSource: BOOLEAN;
  14. oberonDoku: BOOLEAN;
  15. name: Files.FileName
  16. END;
  17. VAR
  18. code: ARRAY 64 OF CHAR;
  19. revCode: ARRAY 256 OF SHORTINT;
  20. table: ARRAY N OF SHORTINT;
  21. PROCEDURE ShowError( n: LONGINT );
  22. BEGIN
  23. Log.String( "### StdCoder.Decode: " );
  24. CASE n OF
  25. | 1: Log.String("bad characters or EOT" );
  26. | 2: Log.String("checksum error" );
  27. | 3: Log.String("incompatible version" );
  28. | 4: Log.String("filing error" );
  29. | 5: Log.String("directory ^0 not found" );
  30. | 6: Log.String("file ^0 not found" );
  31. | 7: Log.String("illegal path" );
  32. | 8: Log.String("bad tag" );
  33. | 9: Log.String("disk write protected" );
  34. | 10: Log.String("io error" );
  35. END;
  36. Log.Ln
  37. END ShowError;
  38. PROCEDURE read( r: Streams.Reader; VAR res: WORD ): LONGINT;
  39. VAR ch: CHAR; x: LONGINT;
  40. BEGIN
  41. IF res = 0 THEN
  42. REPEAT r.Char( ch ); x := revCode[ORD( ch )] UNTIL (x >= 0) OR (r.res # Ok);
  43. IF r.res # Ok THEN res := 1 END
  44. END;
  45. IF res # 0 THEN x := 0 END;
  46. RETURN x
  47. END read;
  48. PROCEDURE peeknext( r: Streams.Reader ): CHAR;
  49. VAR ch: CHAR;
  50. BEGIN
  51. r.Char( ch ); (* consume *) RETURN r.Peek()
  52. END peeknext;
  53. PROCEDURE ReadHeader( r: Streams.Reader;
  54. VAR res: WORD; VAR name: ARRAY OF CHAR; VAR type: LONGINT );
  55. VAR
  56. x, bits, i: LONGINT; ch: CHAR; tag: ARRAY 16 OF CHAR;
  57. BEGIN
  58. tag := Tag; i := 0; res := 0;
  59. r.SkipWhitespace; ch := r.Peek( );
  60. IF ch = tag[0] THEN
  61. ch := peeknext( r ); i := 1;
  62. WHILE (tag[i] # 0X) & (ch = tag[i]) DO ch := peeknext( r ); INC( i ) END;
  63. IF tag[i] # 0X THEN res := 8 END
  64. END;
  65. IF res = 0 THEN
  66. i := 0; bits := 0; x := 0;
  67. REPEAT
  68. WHILE (res = 0) & (bits < 8) DO
  69. INC( x, ASH( read( r, res ), bits ) ); INC( bits, 6 )
  70. END;
  71. IF res = 0 THEN
  72. ch := CHR(x MOD 256); x := x DIV 256; DEC( bits, 8 );
  73. name[i] := ch; INC(i);
  74. END
  75. UNTIL (res # 0) OR (ch = 0X);
  76. i := read(r, res);
  77. IF res = 0 THEN
  78. IF i IN {OldVersion, ThisVersion} THEN type := read( r, res )
  79. ELSE res := 3
  80. END
  81. END
  82. ELSE res := 8
  83. END
  84. END ReadHeader;
  85. PROCEDURE SkipFileType(r: Streams.Reader; VAR res: WORD ): BOOLEAN;
  86. VAR x, bits, i: LONGINT; ch: CHAR; ftype: FileType;
  87. BEGIN
  88. res := 0; i := 0; bits := 0; x := 0;
  89. REPEAT
  90. WHILE (res = 0) & (bits < 8) DO INC( x, ASH( read( r, res ), bits ) ); INC( bits, 6 ) END;
  91. IF res = 0 THEN
  92. ch := CHR( x MOD 256 ); x := x DIV 256; DEC( bits, 8 ); ftype[i] := ch; INC( i )
  93. END
  94. UNTIL (res # 0) OR (ch = 0X);
  95. RETURN res = 0
  96. END SkipFileType;
  97. PROCEDURE ReadInteger( r: Streams.Reader; VAR res: WORD ): LONGINT;
  98. VAR bits, val: LONGINT;
  99. BEGIN
  100. bits := 0; val := 0;
  101. REPEAT val := val + ASH( read( r, res ), bits ); INC( bits, 6 ) UNTIL (res # 0) OR (bits >= 32);
  102. RETURN val
  103. END ReadInteger;
  104. PROCEDURE ReadFile( r: Streams.Reader; VAR res: WORD ): Files.File;
  105. VAR
  106. hash, x, bits, i, len, sum, s: LONGINT; byte: SHORTINT; f: Files.File; w: Files.Writer;
  107. BEGIN
  108. f := Files.New( "" ); Files.OpenWriter( w, f, 0 );
  109. FOR i := 0 TO N - 1 DO table[i] := 0 END;
  110. bits := 0; hash := 0; sum := 0; len := ReadInteger( r, res );
  111. WHILE (res = 0) & (len # 0) DO
  112. IF bits = 0 THEN x := read( r, res ); bits := 6 END;
  113. IF ODD( x ) THEN (* Incorrect prediction -> 1'xxxx'xxxx *)
  114. x := x DIV 2; DEC( bits );
  115. WHILE (res = 0) & (bits < 8) DO INC( x, ASH( read( r, res ), bits ) ); INC( bits, 6 ) END;
  116. i := x MOD 256;
  117. IF i > MAX(SHORTINT) THEN i := i - 256 END;
  118. byte := SHORTINT( i ); x := x DIV 256; DEC( bits, 8 );
  119. table[hash] := byte
  120. ELSE (* correct prediction *)
  121. byte := table[hash]; x := x DIV 2; DEC( bits )
  122. END;
  123. hash := (16 * hash + byte MOD 256) MOD N;
  124. sum := (sum + byte MOD 256) MOD (16 * 1024);
  125. w.Char( CHR( byte ) ); DEC( len );
  126. END;
  127. IF res = 0 THEN
  128. s := ReadInteger( r, res );
  129. IF (res = 0) & (s # sum) THEN res := 2 END
  130. END;
  131. w.Update;
  132. RETURN f
  133. END ReadFile;
  134. PROCEDURE NewFile( CONST name: ARRAY OF CHAR ): Files.File;
  135. VAR
  136. f: Files.File; res: WORD; name2: Files.FileName;
  137. BEGIN
  138. f := Files.Old( name );
  139. IF f # NIL THEN
  140. COPY( name, name2 ); Strings.Append( name2, ".Bak" ); Files.Rename( name, name2, res );
  141. Log.String( "Backup created in " ); Log.String( name2 ); Log.Ln
  142. END;
  143. RETURN Files.New( name );
  144. END NewFile;
  145. PROCEDURE CutSuffixODC( VAR n: Files.FileName );
  146. VAR i: LONGINT
  147. BEGIN
  148. i := 0; WHILE n[i] # 0X DO INC(i) END;
  149. IF (i > 4) & (n[i -4] = ".") & (CAP(n[i-3]) = "O") & (CAP(n[i-2]) = "D") & (CAP(n[i-1]) = "C") THEN
  150. n[i - 4] := 0X
  151. END;
  152. END CutSuffixODC;
  153. PROCEDURE PositionReader( VAR r: Files.Reader; str: ARRAY OF CHAR );
  154. VAR pos, i: LONGINT; c: CHAR;
  155. BEGIN
  156. pos := 0;
  157. LOOP
  158. r.SetPos( pos );
  159. REPEAT r.Char( c ); INC( pos ) UNTIL (c = str[0]) OR (r.res # Ok);
  160. i := 1;
  161. LOOP
  162. IF r.res = Ok THEN r.Char( c ) ELSE (*failed*) r.SetPos( 0 ); RETURN END;
  163. IF c # str[i] THEN (*try again*) EXIT END;
  164. INC( i );
  165. IF str[i] = 0X THEN (* found *) r.SetPos( pos - 1 ); RETURN END
  166. END
  167. END
  168. END PositionReader;
  169. PROCEDURE ExtractAscii( src: Files.File; CONST startstring, destname: ARRAY OF CHAR );
  170. CONST CR = 0DX; LF = 0AX; HT = 09X;
  171. VAR r: Files.Reader; w: Files.Writer; destfile: Files.File; ch: CHAR;
  172. BEGIN
  173. Log.String( "extract: " ); Log.String( destname ); Log.Ln;
  174. destfile := NewFile( destname ); Files.OpenWriter( w, destfile, 0 );
  175. Files.OpenReader( r, src, 0 ); PositionReader( r, startstring );
  176. r.Char( ch );
  177. REPEAT
  178. IF (ch = CR) OR (ch = LF) THEN w.Ln
  179. ELSIF ((ch >= ' ') & (ch <= '~')) OR (ch = HT) THEN w.Char( ch )
  180. END;
  181. r.Char( ch )
  182. UNTIL r.res # Ok;
  183. w.Update; Files.Register( destfile )
  184. END ExtractAscii;
  185. PROCEDURE DecodeFile( r: Streams.Reader; CONST name: Files.FileName );
  186. VAR res: WORD; f: Files.File;
  187. BEGIN
  188. IF SkipFileType( r, res ) THEN
  189. f := ReadFile( r, res );
  190. IF res = 0 THEN ExtractAscii( f, "???????", name )
  191. ELSE ShowError( res )
  192. END
  193. ELSE ShowError( res )
  194. END
  195. END DecodeFile;
  196. PROCEDURE DecodeFileList ( r: Streams.Reader; VAR res: WORD );
  197. VAR
  198. i: LONGINT; p, files: FileList; ch: CHAR;
  199. f: Files.File; fr: Files.Reader; path: Files.FileName;
  200. sa: Strings.StringArray; suffix: ARRAY 16 OF CHAR;
  201. BEGIN
  202. IF SkipFileType( r, res ) THEN
  203. f := ReadFile( r, res );
  204. IF res = 0 THEN
  205. files := NIL; p := NIL;
  206. Files.OpenReader( fr, f, 0 );
  207. fr.Char( ch );
  208. WHILE (fr.res = Ok) & (res = 0) DO
  209. i := 0;
  210. WHILE (fr.res = Ok) & (ch # 0X) DO path[i] := ch; INC( i ); fr.Char( ch ) END;
  211. path[i] := 0X;
  212. CutSuffixODC( path );
  213. sa := Strings.Split( path, '/' );
  214. IF LEN( sa ) > 1 THEN
  215. COPY( sa[LEN( sa ) - 1]^, path );
  216. COPY( sa[LEN( sa ) - 2]^, suffix );
  217. Strings.Append( path, '.' ); Strings.Append( path, suffix )
  218. END;
  219. IF fr.res=Ok THEN
  220. IF p = NIL THEN NEW( p ); files := p ELSE NEW( p.next ); p := p.next END;
  221. p.oberonSource := suffix = "Mod";
  222. p.oberonDoku := suffix = "Docu";
  223. p.name := path;
  224. fr.Char( ch )
  225. ELSE res := 1
  226. END
  227. END;
  228. p := files;
  229. WHILE (res = 0) & (p # NIL) DO
  230. IF SkipFileType( r, res ) THEN
  231. f := ReadFile( r, res );
  232. IF res = 0 THEN
  233. IF p.oberonSource THEN ExtractAscii( f, "MODULE", p.name )
  234. ELSIF p.oberonDoku THEN ExtractAscii( f, "DEFINITION", p.name )
  235. ELSE (* ignore *)
  236. END
  237. END
  238. END;
  239. p := p.next
  240. END
  241. END
  242. END
  243. END DecodeFileList;
  244. PROCEDURE Decode* ( c: Commands.Context );
  245. VAR
  246. name: Files.FileName;
  247. res, type: LONGINT;
  248. r: Streams.Reader;
  249. BEGIN
  250. r := c.arg;
  251. ReadHeader( r, res, name, type );
  252. CutSuffixODC( name );
  253. IF res = 0 THEN
  254. IF type = View THEN (*DecodeView(rd, name) *) DecodeFile( r, name )
  255. ELSIF type = File THEN DecodeFile( r, name )
  256. ELSIF type = List THEN DecodeFileList( r, res );
  257. IF res # 0 THEN ShowError( res ) END
  258. ELSE ShowError( 3 )
  259. END
  260. ELSE ShowError( res )
  261. END
  262. END Decode;
  263. PROCEDURE InitCodes;
  264. VAR i: SHORTINT; j: LONGINT;
  265. BEGIN
  266. FOR j := 0 TO 255 DO revCode[j] := -1 END;
  267. code[0] := "."; revCode[ORD(".")] := 0;
  268. code[1] := ","; revCode[ORD(",")] := 1;
  269. i := 2; j := ORD("0");
  270. WHILE j <= ORD("9") DO code[i] := CHR(j); revCode[j] := i; INC(i); INC(j) END;
  271. j := ORD("A");
  272. WHILE j <= ORD("Z") DO code[i] := CHR(j); revCode[j] := i; INC(i); INC(j) END;
  273. j := ORD("a");
  274. WHILE j <= ORD("z") DO code[i] := CHR(j); revCode[j] := i; INC(i); INC(j) END;
  275. ASSERT(i = 64, 60)
  276. END InitCodes;
  277. BEGIN
  278. InitCodes
  279. END StdCoder.
  280. System.Free StdCoder ~