Guest User

Roger Pepitone

a guest
Jul 21st, 2010
364
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. An LZE / Le file consists of the following:
  2. - A six byte header
  3. - A series of blocks
  4.  
  5.  
  6. The header consists of:
  7. - 2 bytes: 0x4c, 0x65 ("Le" in ASCII)
  8. - 4 bytes: the size of the uncompressed data in little-endian format
  9.  
  10. Each block consists of:
  11. - 1 byte: the types for the following mini-records
  12. (2 bits per type, stored with the first type at the least
  13. significant bit)
  14. - 4 mini-records
  15.  
  16.  
  17. Each mini-record consists of:
  18. - If its type is 0:
  19. -- 2 bytes BYTE1 BYTE2: Write (3 + (BYTE2 >> 4)) bytes from
  20. back (5 + (BYTE1 | ((BYTE2 & 0xf) << 8))) to output
  21. - If its type is 1:
  22. -- 1 byte BYTE1: Write (2 + (BYTE >> 2)) bytes from
  23. back (1 + (BYTE & 3)) to output
  24. - If its type is 2:
  25. -- 1 byte: (copied to output stream)
  26. - If its type is 3:
  27. -- 3 bytes: (copied to output stream)
  28.  
  29.  
  30. The last block my go over the end; if so, ignore any excess data.
  31.  
  32.  
  33. ########################################################################
  34.  
  35. sub unlze {
  36. my @dat = unpack ("C*", ${$_[0]});
  37. if ($dat[0] != 0x4c || $dat[1] != 0x65) { return (0, "Not LZ4c65 format"); }
  38. my $size = unpack ("V", substr (${$_[0]}, 2, 4));
  39. print "Expecting a total of $size\n";
  40. my @out = ();
  41. my $in = 6;
  42.  
  43. while ($in < @dat && @out < $size) {
  44. my $block_types = $dat[$in++];
  45. for (my $i = 0; $i < 4; ++ $i) {
  46. my $this_type = $block_types & 0x3;
  47. $block_types >>= 2;
  48.  
  49. if ($this_type == 0) {
  50. my $n = ($dat[$in]) | ($dat[$in + 1] << 8);
  51. $in += 2;
  52. my $bytes_copied = 3 + ($n >> 12);
  53. my $offset = 5 + ($n & 0xfff);
  54. if ($offset > @out) {
  55. return (0, "Out of bounds backbuffer");
  56. }
  57. for (my $i = 0; $i < $bytes_copied; ++ $i) {
  58. push @out, $out[-$offset];
  59. }
  60. } elsif ($this_type == 1) {
  61. my $n = $dat[$in++];
  62. my $bytes_copied = 2 + ($n >> 2);
  63. my $offset = 1 + ($n & 3);
  64. if ($offset > @out) {
  65. return (0, "Out of bounds backbuffer");
  66. }
  67. for (my $i = 0; $i < $bytes_copied; ++ $i) {
  68. push @out, $out[-$offset];
  69. }
  70. } elsif ($this_type == 2) {
  71. push @out, $dat[$in++];
  72. } elsif ($this_type == 3) {
  73. push @out, $dat[$in++];
  74. push @out, $dat[$in++];
  75. push @out, $dat[$in++];
  76. } else {
  77. return (0, "Logic error");
  78. }
  79. }
  80. }
  81. if (@out >= $size && @out < $size + 16) {
  82. print "Success\n";
  83. return (1, pack ("C*", @out[0..$size-1]));
  84. }
  85. if (@out < $size) {
  86. return (0, "Output not big enough");
  87. }
  88. return (0, "Unknown error");
  89. }
  90.  
  91. sub compress_lze {
  92. #my @dat = unpack ("C*", ${$_[0]});
  93. my @dat = split //, ${$_[0]};
  94. my $size = scalar @dat;
  95. my @out = ("Le".pack ("V", $size));
  96.  
  97. my $in_ptr = 0;
  98. my @buffer = ();
  99. my %cache = ();
  100. while ($in_ptr < @dat) {
  101. my $next_in_ptr;
  102. my $ch = $dat[$in_ptr];
  103. my $ch3 = substr (${$_[0]}, $in_ptr, 3);
  104. my $lst = $cache{$ch3};
  105. my @lst = defined $lst ? @$lst : ();
  106. while (@lst > 0 && $lst[0] < $in_ptr - 0xfff - 5) {
  107. shift @lst;
  108. }
  109.  
  110. # Check for a one-byte backcopy (offset 1 to 4, length 2 to 41)
  111. my ($best_offA, $best_lenA) = (0, 0);
  112. my ($off, $len);
  113. for ($off = -4; $off < 0; ++ $off) {
  114. next if ($off + $in_ptr < 0);
  115. for ($len = 0; $len <= 0x41 && $len + $in_ptr < $size &&
  116. $dat[$len + $in_ptr] eq $dat[$len + $off + $in_ptr]; ++ $len) {
  117. # Do Nothing
  118. }
  119. if ($len == 0x42) { -- $len; }
  120. if ($len > 1 && $len > $best_lenA) { $best_offA = -$off; $best_lenA = $len; }
  121. }
  122.  
  123. my ($best_offB, $best_lenB) = (0, 0);
  124.  
  125. # Check for a two_byte backcopy (offset 5 to 5+0xfff, length 3 to 18)
  126. if ($best_lenA < 19) {
  127. foreach my $x (@lst) {
  128. $off = $x - $in_ptr;
  129. for ($len = 0; $len <= 18 && $len + $in_ptr < $size &&
  130. $dat[$len + $in_ptr] eq $dat[$len + $off + $in_ptr];
  131. ++ $len) {
  132. # Do nothing
  133. }
  134. if ($len == 19) { -- $len; last; }
  135. }
  136. if ($len > 2 && $len > $best_lenB) { $best_lenB = $len; $best_offB = -$off; }
  137. }
  138.  
  139. if ($best_lenB >= 3 && $best_offB > 4 && $best_lenB > $best_lenA + 1) {
  140. my $n = (($best_lenB - 3) << 12) | ($best_offB - 5);
  141. push @buffer, [ 0, pack ('v', $n) ];
  142. $next_in_ptr = $in_ptr + $best_lenB;
  143. } elsif ($best_lenA > 1) {
  144. my $n = (($best_lenA - 2) << 2) | ($best_offA - 1);
  145. push @buffer, [ 1, chr($n) ];
  146. $next_in_ptr = $in_ptr + $best_lenA;
  147. } else {
  148. if (@buffer > 2 && $buffer[-1][0] == 2 && $buffer[-2][0] == 2) {
  149. pop @buffer;
  150. pop @buffer;
  151. push @buffer, [ 3, join ('', @dat[$in_ptr-2..$in_ptr]) ];
  152. } else {
  153. push @buffer, [ 2, $ch ];
  154. }
  155. $next_in_ptr = $in_ptr + 1;
  156. }
  157. push @lst, $in_ptr;
  158. $cache{$ch3} = \@lst;
  159. ++ $in_ptr;
  160. while ($in_ptr < $next_in_ptr) {
  161. push @{$cache{(substr (${$_[0]}, $in_ptr, 3))}}, $in_ptr;
  162. ++ $in_ptr;
  163. }
  164. }
  165. while ((scalar @buffer) % 4 != 0) {
  166. push @buffer, [ 2, "\0" ];
  167. }
  168. for (my $k = 0; $k < @buffer; $k += 4) {
  169. my $type = 0;
  170. my $str = '';
  171. for (my $i = 0; $i < 4; ++ $i) {
  172. $type |= ($buffer[$k + $i][0]) << (2*$i);
  173. $str .= $buffer[$k+$i][1];
  174. }
  175. push @out, chr($type).$str;
  176. }
  177. return join ('', @out);
  178. }
RAW Paste Data