ff7tk  0.02
Toolkit for making FF7 Tools
LZS.cpp
Go to the documentation of this file.
1 /****************************************************************************/
2 // copyright 2012 Jérôme Arzel <myst6re@gmail.com> //
3 // //
4 // This file is part of FF7tk //
5 // //
6 // FF7tk is free software: you can redistribute it and/or modify //
7 // it under the terms of the GNU General Public License as published by //
8 // the Free Software Foundation, either version 3 of the License, or //
9 // (at your option) any later version. //
10 // //
11 // FF7tk is distributed in the hope that it will be useful, //
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of //
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
14 // GNU General Public License for more details. //
15 /****************************************************************************/
16 /***************************************************************
17  4/6/1989 Haruhiko Okumura
18  Use, distribute, and modify this program freely.
19  Please send me your improved versions.
20  PC-VAN SCIENCE
21  NIFTY-Serve PAF01022
22  CompuServe 74050,1022
23 **************************************************************/
24 #include "LZS.h"
25 
26 qint32 LZS::match_length=0;//of longest match. These are set by the InsertNode() procedure.
27 qint32 LZS::match_position=0;
28 qint32 LZS::lson[4097];//left & right children & parents -- These constitute binary search trees.
29 qint32 LZS::rson[4353];
30 qint32 LZS::dad[4097];
31 unsigned char LZS::text_buf[4113];//ring buffer of size 4096, with extra 17 bytes to facilitate string comparison
32 QByteArray LZS::result;
33 
34 const QByteArray &LZS::decompress(const QByteArray &data, int max)
35 {
36  return decompress(data.constData(), data.size(), max);
37 }
38 
39 const QByteArray &LZS::decompress(const char *data, int fileSize, int max)
40 {
41  int curResult=0, sizeAlloc=max+10;
42  quint16 curBuff=4078, adresse, premOctet=0, i, length;
43  const quint8 *fileData = (const quint8 *)data;
44  const quint8 *endFileData = fileData + fileSize;
45 
46  // Impossible case
47  if(quint64(sizeAlloc) > 2000 * quint64(fileSize)) {
48  qWarning() << "LZS::decompress impossible ratio case" << sizeAlloc << 2000 * quint64(fileSize);
49  result.clear();
50  return result;
51  }
52 
53  if(result.size() < sizeAlloc) {
54  try {
55  result.resize(sizeAlloc);
56  } catch(std::bad_alloc) {
57  result.clear();
58  return result;
59  }
60  }
61 
62  memset(text_buf, 0, 4078);//Le buffer de 4096 octets est initialisé à 0
63 
64  forever
65  {
66  if(((premOctet >>= 1) & 256) == 0) {
67  premOctet = *fileData++ | 0xff00;//On récupère le premier octet puis on avance d'un octet
68  }
69 
70  if(fileData >= endFileData || curResult >= max) {
71  result.truncate(curResult);
72  return result;//Fini !
73  }
74 
75  if(premOctet & 1)
76  {
77  result[curResult] = text_buf[curBuff] = *fileData++;//On récupère l'octet (qui n'est pas compressé) et on le sauvegarde dans la chaine finale (result) et dans le buffer. Et bien sûr on fait avancer les curseurs d'un octet.
78  curBuff = (curBuff + 1) & 4095;//Le curseur du buffer doit toujours être entre 0 et 4095
79  ++curResult;
80  }
81  else
82  {
83 // quint16 twoBytes = *((const quint16 *)fileData);
84 // adresse = (twoBytes & 0x00FF) | ((twoBytes & 0xF000) >> 4);
85 // length = ((twoBytes & 0x0F00) >> 8) + 2 + adresse;
86 // fileData += 2;
87 
88  adresse = *fileData++;
89  length = *fileData++;
90  adresse |= (length & 0xF0) << 4;//on récupère l'adresse dans les deux octets (qui sont "compressés")
91  length = (length & 0xF) + 2 + adresse;
92 
93  for(i=adresse ; i<=length ; ++i)
94  {
95  result[curResult] = text_buf[curBuff] = text_buf[i & 4095];//On va chercher l'octet (qui est décompressé) dans le buffer à l'adresse indiquée puis on le sauvegarde dans la chaine finale et le buffer.
96  curBuff = (curBuff + 1) & 4095;
97  ++curResult;
98  }
99  }
100  }
101 }
102 
103 const QByteArray &LZS::decompressAll(const QByteArray &data)
104 {
105  return decompressAll(data.constData(), data.size());
106 }
107 
108 const QByteArray &LZS::decompressAll(const char *data, int fileSize)
109 {
110  int curResult=0, sizeAlloc=fileSize*5;
111  quint16 curBuff=4078, adresse, premOctet=0, i, length;
112  const quint8 *fileData = (const quint8 *)data;
113  const quint8 *endFileData = fileData + fileSize;
114 
115  if(result.size() < sizeAlloc) {
116  try {
117  result.resize(sizeAlloc);
118  } catch(std::bad_alloc) {
119  result.clear();
120  return result;
121  }
122  }
123 
124  memset(text_buf, 0, 4078);//Le buffer de 4096 octets est initialisé à 0
125 
126  forever
127  {
128  if(((premOctet >>= 1) & 256) == 0) {
129  premOctet = *fileData++ | 0xff00;//On récupère le premier octet puis on avance d'un octet
130  }
131 
132  if(fileData >= endFileData) {
133  result.truncate(curResult);
134  return result;//Fini !
135  }
136 
137  if(premOctet & 1)
138  {
139  result[curResult] = text_buf[curBuff] = *fileData++;//On récupère l'octet (qui n'est pas compressé) et on le sauvegarde dans la chaine finale (result) et dans le buffer. Et bien sûr on fait avancer les curseurs d'un octet.
140  curBuff = (curBuff + 1) & 4095;//Le curseur du buffer doit toujours être entre 0 et 4095
141  ++curResult;
142  }
143  else
144  {
145 // quint16 twoBytes = *((const quint16 *)fileData);
146 // adresse = (twoBytes & 0x00FF) | ((twoBytes & 0xF000) >> 4);
147 // length = ((twoBytes & 0x0F00) >> 8) + 2 + adresse;
148 // fileData += 2;
149 
150  adresse = *fileData++;
151  length = *fileData++;
152  adresse |= (length & 0xF0) << 4;//on récupère l'adresse dans les deux octets (qui sont "compressés")
153  length = (length & 0xF) + 2 + adresse;
154 
155  for(i=adresse ; i<=length ; ++i)
156  {
157  result[curResult] = text_buf[curBuff] = text_buf[i & 4095];//On va chercher l'octet (qui est décompressé) dans le buffer à l'adresse indiquée puis on le sauvegarde dans la chaine finale et le buffer.
158  curBuff = (curBuff + 1) & 4095;
159  ++curResult;
160  }
161  }
162  }
163 }
164 
165 const QByteArray &LZS::decompressAllWithHeader(const QByteArray &data)
166 {
167  return decompressAllWithHeader(data.constData(), data.size());
168 }
169 
170 const QByteArray &LZS::decompressAllWithHeader(const char *data, int size)
171 {
172  if (size < 4) {
173  result.clear();
174  return result;
175  }
176 
177  qint32 lzsSize;
178  memcpy(&lzsSize, data, 4);
179 
180  if (lzsSize != size - 4) {
181  result.clear();
182  return result;
183  }
184 
185  return LZS::decompressAll(data + 4, lzsSize);
186 }
187 
188 void LZS::InsertNode(qint32 r)
189 {
190  /* Inserts string of length 18, text_buf[r..r+18-1], into one of the trees (text_buf[r]'th tree) and returns the longest-match position and length via the global variables match_position and match_length.
191  If match_length = 18, then removes the old node in favor of the new one, because the old one will be deleted sooner.
192  Note r plays double role, as tree node and position in buffer. */
193 
194  qint32 i, p, cmp;
195  unsigned char *key;
196 
197  cmp = 1;
198  key = &text_buf[r];
199  p = 4097 + key[0];
200 
201  rson[r] = lson[r] = 4096;
202  match_length = 0;
203 
204  forever
205  {
206  if (cmp >= 0)
207  {
208  if(rson[p] != 4096)
209  {
210  p = rson[p];
211  }
212  else
213  {
214  rson[p] = r;
215  dad[r] = p;
216  return;
217  }
218  }
219  else
220  {
221  if(lson[p] != 4096)
222  {
223  p = lson[p];
224  }
225  else
226  {
227  lson[p] = r;
228  dad[r] = p;
229  return;
230  }
231  }
232 
233  for(i=1 ; i<18 ; i++)
234  {
235  if((cmp = key[i] - text_buf[p + i]) != 0) break;
236  }
237 
238  if(i > match_length)
239  {
240  match_position = p;
241  if((match_length = i) >= 18) break;
242  }
243  }
244 
245  dad[r] = dad[p];
246  lson[r] = lson[p];
247  rson[r] = rson[p];
248 
249  dad[lson[p]] = r;
250  dad[rson[p]] = r;
251 
252  if(rson[dad[p]] == p)
253  {
254  rson[dad[p]] = r;
255  }
256  else
257  {
258  lson[dad[p]] = r;
259  }
260  dad[p] = 4096;//remove p
261 }
262 
263 void LZS::DeleteNode(qint32 p)//deletes node p from tree
264 {
265  qint32 q;
266  if(dad[p] == 4096) return;//not in tree
267 
268  if(rson[p] == 4096)
269  {
270  q = lson[p];
271  }
272  else if(lson[p] == 4096)
273  {
274  q = rson[p];
275  }
276  else
277  {
278  q = lson[p];
279  if(rson[q] != 4096)
280  {
281  do
282  {
283  q = rson[q];
284  }
285  while(rson[q] != 4096);
286 
287  rson[dad[q]] = lson[q];
288  dad[lson[q]] = dad[q];
289  lson[q] = lson[p];
290  dad[lson[p]] = q;
291  }
292  rson[q] = rson[p];
293  dad[rson[p]] = q;
294  }
295  dad[q] = dad[p];
296 
297  if(rson[dad[p]] == p)
298  {
299  rson[dad[p]] = q;
300  }
301  else
302  {
303  lson[dad[p]] = q;
304  }
305  dad[p] = 4096;
306 }
307 
308 const QByteArray &LZS::compressWithHeader(const QByteArray &fileData)
309 {
310  return compressWithHeader(fileData.constData(), fileData.size());
311 }
312 
313 const QByteArray &LZS::compressWithHeader(const char *data, int sizeData)
314 {
315  compress(data, sizeData);
316  quint32 lzsSize = result.size();
317  result.prepend((char *)&lzsSize, 4);
318  return result;
319 }
320 
321 const QByteArray &LZS::compress(const QByteArray &fileData)
322 {
323  return compress(fileData.constData(), fileData.size());
324 }
325 
326 const QByteArray &LZS::compress(const char *data, int sizeData)
327 {
328  int i, c, len, r, s, code_buf_ptr,
329  curResult = 0, sizeAlloc = sizeData / 2;
330  unsigned char code_buf[17], mask;
331  const char *dataEnd = data + sizeData;
332 
333  if(result.size() < sizeAlloc) {
334  result.resize(sizeAlloc);
335  }
336 
337  /* quint32
338  textsize = 0,//text size counter
339  codesize = 0;//code size counter */
340 
341  //initialize trees
342  /* For i = 0 to 4095, rson[i] and lson[i] will be the right and left children of node i. These nodes need not be initialized.
343  Also, dad[i] is the parent of node i. These are initialized to 4096 which stands for 'not used.'
344  For i = 0 to 255, rson[4097 + i] is the root of the tree for strings that begin with character i. These are initialized to 4096. Note there are 256 trees. */
345 
346  for(i=4097 ; i<=4352 ; ++i) rson[i] = 4096;
347  for(i=0 ; i<4096 ; ++i) dad[i] = 4096;
348 
349  code_buf[0] = 0;//code_buf[1..16] saves eight units of code, and code_buf[0] works as eight flags, "1" representing that the unit is an unencoded letter (1 byte), "0" a position-and-length pair (2 bytes). Thus, eight units require at most 16 bytes of code.
350 
351  code_buf_ptr = mask = 1;
352 
353  s = 0;
354  r = 4078;
355 
356 // for(i=s ; i<r ; ++i)
357 // text_buf[i] = '\x0';//Clear the buffer with any character that will appear often.
358  memset(text_buf, 0, r);
359 
360  for(len=0 ; len<18 && data<dataEnd ; ++len)
361  text_buf[r + len] = *data++;//Read 18 bytes into the last 18 bytes of the buffer
362  if(/* (textsize = */len/* ) */ == 0) {
363  result.clear();
364  return result;//text of size zero
365  }
366 
367  for(i=1 ; i<=18 ; ++i)
368  InsertNode(r - i);//Insert the 18 strings, each of which begins with one or more 'space' characters. Note the order in which these strings are inserted. This way, degenerate trees will be less likely to occur.
369 
370  InsertNode(r);//Finally, insert the whole string just read. The global variables match_length and match_position are set.
371 
372  do
373  {
374  if(match_length > len)
375  match_length = len;//match_length may be spuriously long near the end of text.
376 
377  if(match_length <= 2)
378  {
379  match_length = 1;//Not long enough match. Send one byte.
380  code_buf[0] |= mask;//'send one byte' flag
381  code_buf[code_buf_ptr++] = text_buf[r];//Send uncoded.
382  }
383  else
384  {
385  code_buf[code_buf_ptr++] = (unsigned char) match_position;
386  code_buf[code_buf_ptr++] = (unsigned char) (((match_position >> 4) & 0xf0) | (match_length - (2 + 1)));//Send position and length pair. Note match_length > 2.
387  }
388 
389  if((mask <<= 1) == 0)//Shift mask left one bit.
390  {
391 // for(i=0 ; i<code_buf_ptr ; ++i)//Send at most 8 units of
392 // result.append(code_buf[i]);//code together
393  result.replace(curResult, code_buf_ptr, (char *)code_buf, code_buf_ptr);
394  curResult += code_buf_ptr;
395  code_buf[0] = 0;
396  code_buf_ptr = mask = 1;
397  }
398 
399  int last_match_length = match_length;
400  for(i=0 ; i < last_match_length && data<dataEnd ; ++i)
401  {
402  c = *data++;
403  DeleteNode(s);//Delete old strings and
404  text_buf[s] = c;//read new bytes
405 
406  if(s < 17)
407  text_buf[s + 4096] = c;//If the position is near the end of buffer, extend the buffer to make string comparison easier.
408 
409  s = (s + 1) & (4095);
410  r = (r + 1) & (4095);
411  //Since this is a ring buffer, increment the position modulo 4096.
412  InsertNode(r);//Register the string in text_buf[r..r+18-1]
413  }
414 
415  while(i++ < last_match_length)//After the end of text,
416  {
417  DeleteNode(s);//no need to read, but
418  s = (s + 1) & (4095);
419  r = (r + 1) & (4095);
420  if(--len)
421  InsertNode(r);//buffer may not be empty.
422  }
423 
424  }
425  while(len > 0);//until length of string to be processed is zero
426 
427  if(code_buf_ptr > 1)//Send remaining code.
428  {
429 // for(i = 0; i < code_buf_ptr ; ++i)
430 // result.append(code_buf[i]);
431  result.replace(curResult, code_buf_ptr, (char *)code_buf, code_buf_ptr);
432  curResult += code_buf_ptr;
433  }
434  result.truncate(curResult);
435  return result;
436 }
437 
439 {
440  result.clear();
441 }
static const QByteArray & decompressAllWithHeader(const QByteArray &data)
Definition: LZS.cpp:165
static unsigned char text_buf[4113]
Definition: LZS.h:53
static void DeleteNode(qint32 p)
Definition: LZS.cpp:263
static qint32 match_position
Definition: LZS.h:49
static QByteArray result
Definition: LZS.h:54
static void InsertNode(qint32 r)
Definition: LZS.cpp:188
static const QByteArray & compressWithHeader(const QByteArray &fileData)
Definition: LZS.cpp:308
static const QByteArray & decompress(const QByteArray &data, int max)
Definition: LZS.cpp:34
static void clear()
Definition: LZS.cpp:438
static qint32 lson[4097]
Definition: LZS.h:50
static qint32 match_length
Definition: LZS.h:48
static qint32 dad[4097]
Definition: LZS.h:52
static const QByteArray & compress(const QByteArray &fileData)
Definition: LZS.cpp:321
static qint32 rson[4353]
Definition: LZS.h:51
static const QByteArray & decompressAll(const QByteArray &data)
Definition: LZS.cpp:103