1 /******************************************************************************
2 *
3 * Copyright (C) 2009, The Gentee Group. All rights reserved.
4 * This file is part of the Gentee open source project - http://www.gentee.com.
5 *
6 * THIS FILE IS PROVIDED UNDER THE TERMS OF THE GENTEE LICENSE ("AGREEMENT").
7 * ANY USE, REPRODUCTION OR DISTRIBUTION OF THIS FILE CONSTITUTES RECIPIENTS
8 * ACCEPTANCE OF THE AGREEMENT.
9 *
10 * Author: Alexey Krivonogov ( gentee )
11 *
12 ******************************************************************************/
13
14 type geacomp
15 {
16 uint idgroup // id of the file group
17 str subfolder // subfolder
18 str password // password
19 uint compmethod // compression algorithms
20 uint order // compression order
21 uint solid // 1 if solid archive
22 }
23
24 // Функция прямой записи в файл
25 method uint geae.write( uint ptr size )
26 {
27 uint iw
28
29 if !WriteFile( this.curhandle, ptr, size, &iw, 0 ) ||
30 iw != size
31 {
32 this.mess( $GEAERR_FILEWRITE, %{ this.curfilename })
33 return 0
34 }
35 // this.volumes[ *this.volumes - 1 ] += long( size )
36 this.volumes[ this.curvol ] = getlongsize( this.curhandle )
37 return 1
38 }
39
40 // Функция записи в файл с учетом разбивки на тома
41 method uint geae.writevolume( uint ptr size )
42 {
43 uint num
44 str stemp
45
46 if !size : return 1
47
48 if !*this.volumes // Первая запись данных
49 {
50 buf btemp
51
52 btemp.expand( $GEA_DESCRESERVE )
53 mzero( btemp.ptr(), $GEA_DESCRESERVE )
54 btemp.use = $GEA_DESCRESERVE
55 this.volumes.expand( 1 )
56 this.volumes[ 0 ] = long( this.geaoff )
57 if this.volsize && this.volsize < long( this.geaoff +
58 $GEA_DESCRESERVE )
59 { // Оставляем первый том полностью пустым
60 this.emptyvol = 1
61 // this.volumes.expand( 1 )
62 // this.curvol = 1
63 }
64 else // Резервируем место
65 {
66 if !this.write( btemp.ptr(), $GEA_DESCRESERVE ) : return 0
67 }
68 }
69 label volume
70 if this.volsize // Проверка на заполнение тома
71 {
72 num = this.curvol //= *this.volumes - 1
73 // print("OK 1 \(this.volumes[ num ]) \(wsize) \(this.volsize)\n")
74 if this.volumes[ num ] + long( size ) >= this.volsize
75 {
76 uint rem
77
78 if this.volsize > this.volumes[ num ] && ( num || !this.emptyvol )
79 { // Дописываем остатки
80 rem = uint( this.volsize - this.volumes[ num ] )
81 if !this.write( ptr, rem ) : return 0
82 }
83 size -= rem
84 ptr += rem
85 // Закрываем том
86 if num : close( this.curhandle )
87 // Создаем новый том
88 num++
89 if num > $GEA_MAXVOLUMES
90 {
91 this.mess( $GEAERR_MANYVOLUMES, %{this.curfilename })
92 return 0
93 }
94 this.curvol = this.volumes.expand( 1 )
95 geavolume gv
96 gv.name = 0x414547
97 gv.unique = this.unique
98 gv.number = num
99 // int2str( stemp.clear(), this.pattern, num + 1 )
100 stemp.clear()
101 stemp.out4( this.pattern, num + 1 )
102 this.volnames += stemp
103 ( this.curfilename = this.volpath ).faddname( stemp )
104 label again
105 if !( this.curhandle = open( this.curfilename, $OP_EXCLUSIVE |
106 $OP_CREATE ))
107 {
108 switch this.mess( $GEAERR_FILEOPEN, %{ this.curfilename })
109 {
110 case $GEA_RETRY : goto again
111 case $GEA_ABORT : return 0
112 }
113 }
114 if !this.write( &gv, sizeof( geavolume )) : return 0
115 goto volume
116 }
117 }
118 if !this.write( ptr, size ) : return 0
119 // congetch("Ooops = \(size)\n")
120 return 1
121 }
122
123 method uint geae.put( uint ptr size )
124 {
125 uint avail
126 uint pout full end start stop
127
128 subfunc uint storebuf( uint rem )
129 {
130 uint iw = full - avail - rem // Сколько записать
131
132 if start + iw < stop
133 {
134 if !this.writevolume( start, iw ) : return 0
135 start += iw
136 }
137 else
138 {
139 if !this.writevolume( start, stop - start ) : return 0
140 iw -= stop - start
141 if !this.writevolume( pout, iw ) : return 0
142 start = pout + iw
143 }
144 if !rem
145 {
146 start = pout
147 end = pout
148 }
149 avail = full - rem
150 return 1
151 }
152 subfunc uint finish()
153 { // Запись оставшихся данных
154 if !*this.volumes
155 { // Записей не было и заголовок уже записан. Пишем все данные
156 this.volumes.expand( 1 )
157 this.volumes[ 0 ] = getlongsize( this.curhandle )
158 // print("Volume = \( this.volumes[ 0 ] )\n")
159 }
160 else // Есть зарезервированное место
161 {
162 if this.emptyvol
163 { // Нет зарезервированного места
164 this.volumes[ 0 ] = getlongsize( this.handle )
165 if this.volsize > this.volumes[ 0 ]
166 {
167 // this.volsize - this.volumes[ 0 ] не больше uint иначе не
168 // может быть emptyvol
169 this.head.movedsize = uint( this.volsize - this.volumes[ 0 ] )
170 }
171 }
172 else
173 {
174 this.head.movedsize = $GEA_DESCRESERVE - this.head.size
175 }
176 setpos( this.curhandle, 0, $FILE_END )
177 // Дописываем остатки
178 if !storebuf( this.head.movedsize ) : return 0
179 if this.curhandle != this.handle : close( this.curhandle )
180 // Дописываем в главный файл
181 this.curhandle = this.handle
182 setpos( this.handle, this.geaoff + this.head.size, $FILE_BEGIN )
183 this.curvol = 0
184 this.volsize = 0L
185 }
186 // Записываем все из буфера в первый том
187 if !storebuf( 0 ) : return 0
188
189 return 1
190 }
191 pout = this.out.ptr()
192 full = *this.out
193 end = this.end
194 start = this.start
195 stop = this.stop
196 avail = ?( end >= start, full - ( end - start ), start - end )
197
198 if !ptr : return finish()
199
200 this.head.summary += long( size )
201 this.fileinfo[ *this.fileinfo - 1 ].compsize += size
202 // print("\n\(this.fileinfo[ *this.fileinfo - 1 ].compsize)\n")
203
204 if size >= avail
205 { // Необходимо записать лишнее на диск
206 if size > $GEA_DESCRESERVE
207 {
208 // Записываем все из буфера
209 if !storebuf( 0 ) : return 0
210 // Записываем часть из ptr
211 if !this.writevolume( ptr, size - $GEA_DESCRESERVE ) : return 0
212 ptr += size - $GEA_DESCRESERVE
213 size = $GEA_DESCRESERVE
214 }
215 else
216 { // Записываем часть из буфера
217 if !storebuf( $GEA_DESCRESERVE - size ) : return 0
218 }
219 }
220 // Записываем данные в буфер
221 if stop - end > size
222 {
223 mcopy( end, ptr, size )
224 end += size
225 }
226 else
227 {
228 uint rem = stop - end
229 mcopy( end, ptr, rem )
230 mcopy( pout, ptr + rem, size - rem )
231 end = pout + size - rem
232 }
233 this.end = end
234 this.start = start
235 return 1
236 }
237
238 method uint geae.add( str filename, geacomp gc )
239 {
240 geaparam gp
241 uint cursize blocksize solidsize
242 uint handle curid gf i
243 str fullname fpath fname
244 buf in out
245
246 fullname.ffullname( filename )
247 fname.fnameext( fullname )
248 fpath.fgetdir( fullname )
249
250 if fpath %== this.volpath // Проверка на имя тома
251 {
252 foreach curvol, this.volnames : if curvol %== fname : return 1
253 }
254 // Проверка на то, что уже добавлен
255 if i = this.addedfiles.find( fullname )
256 {
257 this.mess( $GEAMESS_COPY, %{ fullname })
258 if this.flags & $GEAI_IGNORECOPY : return 1
259 }
260 else
261 { // Добавление в добавленные
262 this.addedfiles[ fullname ] = 1
263 }
264
265 // Открываем файл
266 if !( handle = gea_fileopen( fullname, $OP_READONLY, this.userfunc ))
267 {
268 return 0
269 }
270 // Не принимаем слишком большие файлы
271 if getlongsize( handle ) >= 0xFFFF0000L
272 {
273 close( handle )
274 return this.mess( $GEAERR_TOOBIG, %{ fullname }) == $GEA_IGNORE
275 }
276 curid = this.fileinfo.expand( 1 )
277 gf as this.fileinfo[ curid ]
278 gf.name = fname
279 gf.crc = 0xFFFFFFFF
280 gf.idgroup = gc.idgroup
281 gf.subfolder = gc.subfolder
282 // Получаем время, размер, атрибуты, версию
283 getftime( handle, gf.ft )
284 gf.size = getsize( handle )
285 gf.attrib = getfileattrib( fullname )
286 getfversion( fullname, &gf.hiver, &gf.lowver )
287 if *gc.password
288 { // Есть такой пароль или нет
289 fornum i = 0, *this.passwords
290 {
291 if this.passwords[ i ] == gc.password
292 {
293 gf.idpass = i + 1
294 break
295 }
296 }
297 if !gf.idpass // Если не нашли то добавляем пароль
298 {
299 this.passwords += gc.password
300 gea_passgen( this.passbufs[ this.passbufs.expand( 1 ) ],
301 gc.password )
302 gf.idpass = *this.passwords
303 }
304 }
305 this.mess( $GEAMESS_ENBEGIN, %{ fullname, gf })
306 blocksize = 0x40000 * this.head.blocksize
307 solidsize = 0x40000 * this.head.solidsize
308 in.expand( blocksize + solidsize )
309 out.expand( blocksize + blocksize / 10 )
310
311 uint store failpack leadorder
312 lzge lz
313 ppmd ppm
314
315 gc.order = max( 1, min( 10, gc.order ))
316
317 gp.done = 0
318 gp.name = fullname
319 gp.info = &gf
320 gp.mode = 0
321 switch gc.compmethod
322 {
323 case $GEA_STORE
324 {
325 geadata gd
326
327 gd.order |= 0x80
328 gd.size = gf.size
329 if gf.size
330 {
331 if !this.put( &gd, sizeof( geadata )) : return 0
332 }
333 store = 1
334 }
335 case $GEA_LZGE
336 {
337 lz.order = gc.order
338 lz.userfunc = this.userfunc
339 lz.pgeaparam = &gp
340 }
341 case $GEA_PPMD
342 {
343 // ppm.memory = this.head.memory
344 ppm.order = gc.order + 1
345 ppm.userfunc = this.userfunc
346 ppm.pgeaparam = &gp
347 leadorder = ppm.order
348 }
349 }
350 if gc.solid
351 {
352 // Сбрасываем если упаковали уже много с solid сжатием
353 if this.prevsolid + gf.size > solidsize : this.prevsolid = 0
354
355 // Проверяем на совпадение предыдущих настроек
356 if this.prevsolid && this.prevmethod == gc.compmethod &&
357 this.prevorder == gc.order && gf.idpass == this.prevpass
358 {
359 gf.flags |= $GEAF_SOLID
360 this.prevsolid += gf.size
361 }
362 else : this.prevsolid = gf.size
363 this.prevpass = gf.idpass
364 }
365 else : this.prevsolid = 0
366 // print("Prev=\(this.prevsolid) block=\( blocksize ) size=\( gf.size )\n")
367 if !( gf.flags & $GEAF_SOLID ) : this.bsolid.clear()
368
369 while cursize < gf.size
370 {
371 geadata gd
372 uint issolid pout psize pin
373 uint iread = min( gf.size - cursize, blocksize )
374
375 in.use = solidsize
376 out.use = 0
377 if read( handle, in, iread ) != iread
378 {
379 this.mess( $GEAERR_FILEREAD, %{ fullname })
380 return 0
381 }
382 pin = in.ptr() + solidsize
383 gf.crc = crc( pin, iread, gf.crc )
384 if gc.compmethod == $GEA_LZGE
385 {
386 lz.solidoff = 0//*this.bsolid
387
388 if ( cursize || gf.flags & $GEAF_SOLID ) && !failpack
389 {
390 lz.solidoff = *this.bsolid
391 issolid = 1
392 pin -= lz.solidoff
393 mcopy( pin, this.bsolid.ptr(), lz.solidoff )
394 }
395 out.use = lzge_encode( pin, iread + lz.solidoff, out.ptr(), lz )
396 // out.write("c:\\aa\\encode\(lz.solidoff).bin")
397 i = min( solidsize, iread + lz.solidoff )
398 this.bsolid.copy( pin + iread + lz.solidoff - i, i )
399 // print("\ni= \( i ) iread=\( iread ) ss = \(solidsize) bs=\( *this.bsolid )\n")
400 }
401 elif gc.compmethod == $GEA_PPMD
402 {
403 if ( cursize || gf.flags & $GEAF_SOLID ) && !failpack
404 {
405 ppm.order = 1
406 issolid = 1
407 }
408 out.use = ppmd_encode( pin, iread, out.ptr(), out.size, ppm )
409 ppm.order = leadorder
410 }
411 if gc.compmethod && out.use > ( in.use - solidsize ) * 98 / 100
412 {
413 // print("\n\(in.use) <= \(out.use)\n")
414 if !cursize
415 {
416 gd.order |= 0x80
417 gf.flags &= ~$GEAF_SOLID
418 }
419 gd.size = iread
420 if !this.put( &gd, sizeof( geadata )) : return 0
421 failpack = 1
422 issolid = 0
423 this.bsolid.clear()
424 if gc.compmethod == $GEA_LZGE : pin += lz.solidoff
425 }
426 else : failpack = 0
427 if store || failpack
428 {
429 pout = pin
430 psize = iread
431 this.prevmethod = $GEA_STORE
432 this.prevsolid = 0
433 if store
434 {
435 this.mess( $GEAMESS_PROCESS, %{ fullname, gf, iread + gp.done })
436 }
437 }
438 else
439 {
440 this.prevmethod = gc.compmethod
441 this.prevorder = gc.order
442
443 gd.order = ( gc.compmethod << 4 ) + ?( issolid, 0, gc.order )
444 // print("Order = \(gd.order)\n")
445 if !cursize : gd.order |= 0x80
446
447 gd.size = *out
448 if !this.put( &gd, sizeof( geadata )) : return 0
449 pout = out.ptr()
450 psize = *out
451 }
452 if gf.idpass // Шифруем
453 {
454 gea_protect( pout, psize, this.passbufs[ gf.idpass - 1 ] )
455 }
456 if !this.put( pout, psize ) : return 0
457 cursize += blocksize
458 gp.done += iread
459 }
460 close( handle )
461 this.mess( $GEAMESS_ENEND, %{ fullname, gf })
462 return 1
463 }
464
465 method uint geae.adddir( str filename, geacomp gc )
466 {
467 str fullname fpath fname
468 uint curid i gf
469
470 /* geaparam gp
471 uint cursize blocksize solidsize
472 uint handle curid gf i
473 buf in out
474 */
475 fullname.ffullname( filename )
476 fname.fnameext( fullname )
477 fpath.fgetdir( fullname )
478
479 // Проверка на то, что уже добавлен
480 if i = this.addedfiles.find( fullname )
481 {
482 this.mess( $GEAMESS_COPY, %{ fullname })
483 if this.flags & $GEAI_IGNORECOPY : return 1
484 }
485 else
486 { // Добавление в добавленные
487 this.addedfiles[ fullname ] = 1
488 }
489
490 curid = this.fileinfo.expand( 1 )
491 gf as this.fileinfo[ curid ]
492 gf.name = fname
493 gf.crc = 0xFFFFFFFF
494 gf.idgroup = gc.idgroup
495 gf.subfolder = gc.subfolder
496 // Получаем время, размер, атрибуты, версию
497 gf.attrib = getfileattrib( fullname )
498 this.mess( $GEAMESS_ENBEGIN, %{ fullname, gf })
499
500 this.prevmethod = $GEA_STORE
501 this.prevsolid = 0
502
503 // print("Prev=\(this.prevsolid) block=\( blocksize ) size=\( gf.size )\n")
504 this.bsolid.clear()
505
506 this.mess( $GEAMESS_ENEND, %{ fullname, gf })
507 return 1
508 }
509