1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859
|
//sfArkLib Coding
//Copyright 1998-2000 Andy Inman
//Contact via: http://netgenius.co.uk or http://melodymachine.com
// This file is part of sfArkLib.
//
// sfArkLib is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// sfArkLib is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with sfArkLib. If not, see <http://www.gnu.org/licenses/>.
// V2.11 Initial release (with SDL 0.07)
// V2.12 Bug fix in UnMemcomp (OutBytes was uninitialised causing crash for some files)
// Used for SDL 0.08.
// V2.13 Bug fix, previously file written was corrupt due to wrong buffer pointers
// V2.14 Now using UpdateProgress function (supplied by application)
// V2.15 Now using GetLicenseAgreement & DisplayNotes functions (app-supplied)
// 2.20 Version bumped to 2.20 - First version for MacOS support
// Major re-write to ReadHeader function, supports any size of self-extraction code in sfArk.exe files.
// 2.21 25-09-02 Fixed byte order on output samples (for MacOS).
// 2.22 27-09-03 Fixed bug in call to UpdateProgress which reset to zero with large files when bytes
// written greater than (1<<31)/100 (approx 20MB) -- was due to 32-bit integer overflow
// Added more floating-point precision conversions in LPC to tighten-up on Mac compatibility issues
// Avoid potential bug in WriteOutputFile, now test BytesWritten != BytesToWrite instead of <= 0
// Fixed rare bug when POST_AUDIO is more than ZBUF_SIZE (256kb) uncompressed (affected RealFont_2_1.sfArk)
// 2.23 02-10-03 LPC Speeded up, now double the speed of earlier versions (on Intel at least)
// CLIB headers...
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
// sfArk specific headers...
#define SFARKLIB_GLOBAL
#include "wcc.h"
#include "zlib.h"
// Set to 1 for special debug mode (needs enabled version of compressor)
#define DB_BLOCKCHECK 0
// FileSection equates...
#define RIFF_HEADER 0
#define RIFF_INFO 1
#define INFO_LIST 2
#define SDTA_LIST 3
#define AUDIO_START 4
#define AUDIO 5
#define PRE_AUDIO 6
#define POST_AUDIO 7
#define NON_AUDIO 8
#define FINISHED 9
// Misc equates...
#define OPTWINSIZE 32 // Default window size used by CrunchWin()
#define ZBUF_SIZE (256 * 1024) // Size of buffer used for MemComp (do not change!)
#define NSHIFTS (MAX_BUFSIZE / SHIFTWIN) // Max number of shift values per block (MaxBuf/SHIFTWIN = 4096/64)
#define MAX_DIFF_LOOPS 20 // Max number of BufDif loops
// --- Version 2 File Header Structure ---
#define HDR_NAME_LEN 5
#define HDR_VERS_LEN 5
typedef struct
/*
NB: For compatibilty with sfArk V1, we must store "sfArk" at offset 27 (base 1) in
5 characters and the compression method as one byte immediately afterwards at offset 32.
This will allow sfArk V1 to recoginse this as a .sfArk file (though not decompress it!)
*/
{
uint32_t Flags; // 0-3 Bits 0 & 1 used to indicate presence of Notes and License files
uint32_t OriginalSize; // 4-7 Uncompressed file size
uint32_t CompressedSize; // 8-11 Compressed file size (including header)
uint32_t FileCheck; // 12-15 File Checksum
uint32_t HdrCheck; // 16-19 Header Checksum
uint8_t ProgVersionNeeded; // 20 SfArk version needed to unpack this file (20 = Version 2.0x, etc.)
char ProgVersion[HDR_NAME_LEN]; // 21-25 Version string (nn.nn) that created this file (NOT terminated)
char ProgName[HDR_VERS_LEN]; // 26-30 Signature "sfArk" (not terminated)
uint8_t CompMethod; // 31 Compression Method
uint16_t FileType; // 32-33 Currently always 0 (for SF2)
uint32_t AudioStart; // 34-37 Position in original file of start of audio data
uint32_t PostAudioStart; // 38-41 Position in original file of start any data after audio data (e.g. SF2 parameters)
char FileName[MAX_FILENAME]; // 42-297 Original filename, no path (stored variable length, null terminated)
} V2_FILEHEADER;
// Some extras re. Header structure...
#define V2_FILEHEADER_SIZE 298 // *Actual* size of header data, may be less than sizeof(V2_FILEHEADER)
#define HEADER_HDRCHECK_POS 16 // Byte position of HeaderCheck field in header
#define HEADER_SIG_POS 26 // Byte position of "sfArk" signautre in header
#define HEADER_MAX_OFFSET (128*1024) // Maximum search extent: maximum expected size of "self-extractor" code.
// FileHeader Flags...
#define FLAGS_Notes (1 << 0) // Notes file included
#define FLAGS_License (1 << 1) // License file included
const char LicenseExt[] = ".license.txt"; // File extension for license file
const char NotesExt[] = ".txt"; // File extension for notes file
static BYTE *Zbuf1 = NULL, *Zbuf2 = NULL;
// Data per block, passed to ProcessNextBlock()
typedef struct
{
V2_FILEHEADER FileHeader; // FileHeader structre
int FileSection; // Track current "file section"
int ReadSize; // Number of words to read per block
int MaxLoops; // Max loops for reduction with BufDiff2/3
int MaxBD4Loops; // Max loops for reduction with BufDiff4
int nc; // Number of LPC parameters
int WinSize; // Window size for CrunchWin
AWORD *SrcBuf; // Address of source buffer
AWORD *DstBuf; // Address of destination buffer
ULONG TotBytesWritten; // Total bytes written in file
ULONG FileCheck; // File checksum (accumulated)
AWORD PrevIn[MAX_DIFF_LOOPS]; // Previous values (per loop)
USHORT PrevEncodeCount; // Previous number of loops used
USHORT BD4PrevEncodeCount; // Previous number of loops used for BD4
short PrevShift; // Previous Shift value
short PrevUsedShift; // Previously used (non zero) Shift value
} BLOCK_DATA;
// Messages...
const char CorruptedMsg[] = "- This file appears to be corrupted.";
const char UpgradeMsg[] = "Please see Help/About for information on how to obtain an update.";
// ==============================================================
USHORT GetsfArkLibVersion(void)
{
return (ProgVersionMaj * 10) + ProgVersionMin/10;
}
// ==============================================================
char *ChangeFileExt(char *OutFileName, const char *NewExt, int OutFileNameSize)
{
int n = strlen(OutFileName);
char *p;
for (p = OutFileName+n; *p != '.'; p--)
{
if (*p == '\\' || p <= OutFileName) // No extension found?
{
p = OutFileName + n;
break;
}
}
n = p - OutFileName; // Length of filename without extension
strncpy(p, NewExt, OutFileNameSize-1 - n);
return OutFileName;
}
// ==============================================================
// Read the File Header....
int ReadHeader(V2_FILEHEADER *FileHeader, BYTE *fbuf, int bufsize)
{
int HeaderLen = 0, HdrOffset;
char CreatedByProg[HDR_NAME_LEN +1], CreatedByVersion[HDR_VERS_LEN +1];
ULONG CalcHdrCheck = 0;
BYTE *HdrBuf, *bpFileHeader = (BYTE *) FileHeader;
// Find and process the Header: This could be a plain sfArk file, a self-extracting file or some other (invalid) file.
// Also, it could be a sfArk V1 file, which we can't decompress, but should at least recognise.
// We locate the header by looking for the string "sfArk" within the first HEADER_MAX_OFFSET bytes of the file
// but because self-extractor code is likely to contain that string, we look for its final occurence
// by searching backwards.
// To speed things up, we first check at offset 0 (the case for a standard sfArk file)
// If we think we've found a (V2) header, we veryify it using the Header checksum. If the checksum fails,
// chances are it was a corrupt file, but could conceivably be a non-sfArk file. either way we report it as corrupt.
int fbufsize = HEADER_MAX_OFFSET + V2_FILEHEADER_SIZE; // Amount data to read
if (fbufsize > bufsize) fbufsize = bufsize; // Buffer too small (should never happen)
SetInputFilePosition(0); // set to logical start (maybe a predefined offset)
RETURN_ON_ERROR();
ReadInputFile(fbuf, fbufsize); // Read a chunk of data from the start of the file
RETURN_ON_ERROR();
bool SigFound = false, SeemsV1 = false, HdrCheckDone = false; // Some flags to remember what we're doing
for(int TryOffset = 0; TryOffset < HEADER_MAX_OFFSET; TryOffset++)
{
HdrOffset = (TryOffset == 0)? 0 : HEADER_MAX_OFFSET - TryOffset; // Check offset = 0 first, then backwards from end
BYTE *sigpos = fbuf + HdrOffset + HEADER_SIG_POS;
if (*sigpos != 's' || memcmp(sigpos, "sfArk", 5) != 0) continue;
SigFound = true; // Set a flag to remember that we at least got this far
HdrBuf = fbuf + HdrOffset;
if (V2_FILEHEADER_SIZE != sizeof(V2_FILEHEADER)) // Compare structure size to real size
{
// The compiler has messed with structure (alignment), so copy the data to the structure byte by byte...
BYTE *bptr = HdrBuf; // Point to start
// Copy all fields...
#define CPF(f) memcpy(&(FileHeader->f), bptr, sizeof(FileHeader->f)); bptr += sizeof(FileHeader->f)
CPF(Flags); CPF(OriginalSize); CPF(CompressedSize); CPF(FileCheck); CPF(HdrCheck);
CPF(ProgVersionNeeded); CPF(ProgVersion); CPF(ProgName); CPF(CompMethod);
CPF(FileType); CPF(AudioStart); CPF(PostAudioStart); CPF(FileName);
#undef CPF
if (bptr != HdrBuf+V2_FILEHEADER_SIZE) return (GlobalErrorFlag = SFARKLIB_ERR_OTHER); // Sanity check
}
else
memcpy(bpFileHeader, HdrBuf, V2_FILEHEADER_SIZE); // Copy entire data block to structure
if (FileHeader->CompMethod < COMPRESSION_v2) // Looks like a sfArk V1 file?
{
SeemsV1 = true; // set a flag
continue; // and keep searching
}
// Header->FileName is a null-terminated string, we need it's length to calculate actual length of header data...
FileHeader->FileName[sizeof(FileHeader->FileName) -1] = '\0'; // Ensure strlen finds a terminator (string may be junk)
HeaderLen = V2_FILEHEADER_SIZE - sizeof(FileHeader->FileName) + strlen(FileHeader->FileName) + 1;
// If we get this far, there's a good chance we've got the header...
#ifdef __BIG_ENDIAN__
// FixEndians of all multi-byte integers (currently only relevent to Mac)
#define FIXENDIAN(field) FixEndian(&(FileHeader->field), sizeof(FileHeader->field))
FIXENDIAN(Flags); FIXENDIAN(OriginalSize); FIXENDIAN(CompressedSize);
FIXENDIAN(FileCheck); FIXENDIAN(HdrCheck); FIXENDIAN(FileType);
FIXENDIAN(AudioStart); FIXENDIAN(PostAudioStart);
#undef FIXENDIAN
#endif
// Ok now, we know the HeaderLength and have the FileHeader structure properly populated...
#if 0
// debug, display header...
printf("Flags %lx OrignalSize %ld CompressedSize %ld\n", FileHeader->Flags, FileHeader->OriginalSize, FileHeader->CompressedSize);
printf("FileCheck %lx HdrCheck %lx ProgVersionNeeded %d\n", FileHeader->FileCheck, FileHeader->HdrCheck, FileHeader->ProgVersionNeeded);
printf("AudioStart %ld PostAudioStart %ld Orginal filename %s\n", FileHeader->AudioStart, FileHeader->PostAudioStart, FileHeader->FileName);
#endif
*(uint32_t *)(HdrBuf+HEADER_HDRCHECK_POS) = 0; // Zero-out the HeaderChecksum position in the buffer
CalcHdrCheck = adler32(0, HdrBuf, HeaderLen); // and recalculate the header checksum
HdrCheckDone = true;
if (CalcHdrCheck == FileHeader->HdrCheck) break; // Check passed: Yes, we've found the header!
}
// When we get here, see what happened:
if (SigFound && HdrCheckDone && CalcHdrCheck == FileHeader->HdrCheck) // Everything Ok! File is V2 and valid
; // Fall through to below (everything else is an error)
else if (SeemsV1) // Seems to be a sfArkV1 file
{
sprintf(MsgTxt, "This file was created with sfArk V1, and this program only handles sfArk V2+ files. Unfortunately sfArk V1 uses a proprietary compression algorithm for the non-audio metadata, so we cannot really support that. You might try running the Windows sfArk program from http://melodymachine.com/sfark.htm under Wine.");
msg(MsgTxt, MSG_PopUp);
return (GlobalErrorFlag = SFARKLIB_ERR_INCOMPATIBLE);
}
else if (SigFound) // Apparently a corrupt sfArk file (well, it had "sfArk" in it!)
{
sprintf(MsgTxt, "File Header fails checksum!%s", CorruptedMsg);
msg(MsgTxt, MSG_PopUp);
return (GlobalErrorFlag = SFARKLIB_ERR_HEADERCHECK);
}
else // Either very corrupted, or not a sfArk file
{
sprintf(MsgTxt, "This does not appear to be a sfArk file!");
msg(MsgTxt, MSG_PopUp);
return (GlobalErrorFlag = SFARKLIB_ERR_SIGNATURE);
}
// Get CreatedBy program name and version number (need null-terminated strings)...
strncpy(CreatedByProg, FileHeader->ProgName, HDR_NAME_LEN); // Copy program name
CreatedByProg[HDR_NAME_LEN] = 0; // Terminate string
strncpy(CreatedByVersion, FileHeader->ProgVersion, HDR_VERS_LEN); // Copy version string
CreatedByVersion[HDR_VERS_LEN] = 0; // Terminate string
// Check for compatible version...
if (FileHeader->ProgVersionNeeded > ProgVersionMaj)
{
sprintf(MsgTxt, "You need %s version %2.1f (or higher) to decompress this file (your version is %s) %s",
ProgName, (float)FileHeader->ProgVersionNeeded/10, ProgVersion, UpgradeMsg);
msg(MsgTxt, MSG_PopUp);
return (GlobalErrorFlag = SFARKLIB_ERR_INCOMPATIBLE);
}
// Warn if file was created by a newer version than this version...
float fProgVersion = (float) atof(ProgVersion);
float fCreatedByVersion = (float) atof(CreatedByVersion);
if (fCreatedByVersion > fProgVersion)
{
sprintf(MsgTxt, "This file was created with %s %s. Your version of %s (%s) can uncompress this file, "
"but you might like to obtain the latest version. %s",
CreatedByProg, CreatedByVersion, ProgName, ProgVersion, UpgradeMsg);
msg(MsgTxt, MSG_PopUp);
}
SetInputFilePosition(HdrOffset + HeaderLen); // re-wind file to start of post-header data
RETURN_ON_ERROR();
return SFARKLIB_SUCCESS;
}
// ==============================================================
bool InvalidEncodeCount(int EncodeCount, int MaxLoops)
{
if (EncodeCount < 0 || EncodeCount > MaxLoops) // EncodeCount out of range?
{
sprintf(MsgTxt, "ERROR - Invalid EncodeCount (apparently %d) %s", EncodeCount, CorruptedMsg);
msg(MsgTxt, MSG_PopUp);
return true;
}
else
return false;
}
// ==============================================================
int DecompressTurbo(BLOCK_DATA *Blk, USHORT NumWords)
{
int EncodeCount = InputDiff(Blk->PrevEncodeCount);
if (InvalidEncodeCount(EncodeCount, Blk->MaxLoops)) return (GlobalErrorFlag = SFARKLIB_ERR_CORRUPT);
Blk->PrevEncodeCount = EncodeCount;
int UnCrunchResult = UnCrunchWin(Blk->SrcBuf, NumWords, 8*OPTWINSIZE);
if (UnCrunchResult < 0)
{
sprintf(MsgTxt, "ERROR - UnCrunchWin returned: %d %s", UnCrunchResult, CorruptedMsg);
msg(MsgTxt, MSG_PopUp);
return (GlobalErrorFlag = SFARKLIB_ERR_CORRUPT);
}
for (int j = EncodeCount-1; j >= 0; j--)
{
if (j == 0) Blk->FileCheck = (Blk->FileCheck << 1) + BufSum(Blk->SrcBuf, NumWords);
UnBufDif2(Blk->DstBuf, Blk->SrcBuf, NumWords, &(Blk->PrevIn[j]));
AWORD *SwapBuf = Blk->SrcBuf; Blk->SrcBuf = Blk->DstBuf; Blk->DstBuf = SwapBuf;
}
return SFARKLIB_SUCCESS;
}
// ==============================================================
bool CheckShift(short *ShiftVal, USHORT NumWords, short *PrevShift, short *PrevUsedShift)
// Here we look to see if the current buffer has been rightshifted
// There is a flag for the whole buffer to show if any shifted data exists,
// and if so there further flags to show if the Shift value changes within each sub block
// of ShiftWin words.
// For any changes, then follows the sub-block number where change occurs, then the new
// ShiftValue. The latter is given either as absolute value (if shift was previously
// non-zero) or relative change from previously used value (if shift was previously zero)
// For non-zero shift value, we then need to leftshift the data by that number of bits.
{
#define ShiftWin 64 // Size of window for Shift
bool UsingShift = BioReadFlag(); // Read flag to see if using any shift
if (UsingShift) // and if so...
{
int MaxShifts = (NumWords+ShiftWin-1) / ShiftWin; // Number of ShiftWin sized sub-blocks in this block
int ChangePos = 0; // Init. position of last change
int p = 0;
while (BioReadFlag()) // Read flag to see if there is a (further) change of shift value
{
// Read position of new shift value...
int nb = GetNBits(MaxShifts - ChangePos -1); // number of possible bits for ChangePos
ChangePos = BioRead(nb) + ChangePos; // Get position of next change of shift value
// Read value of new shift...
short NewShift;
if (*PrevShift == 0) // If previous shift was 0
{
NewShift = InputDiff(*PrevUsedShift); // Get new shift as diff from last used shift
*PrevUsedShift = NewShift; // Update PrevUsedShift
}
else // Else
NewShift = InputDiff(0); // Get new shift as difference from 0
// Update all ShiftVal[] data prior to change...
if (ChangePos > MaxShifts) // Corrupt data?
{
sprintf(MsgTxt, "ERROR - Invalid Shift ChangePos (apparently %d) %s", ChangePos, CorruptedMsg);
msg(MsgTxt, MSG_PopUp);
GlobalErrorFlag = SFARKLIB_ERR_CORRUPT;
return false;
}
for ( ; p < ChangePos; p++) ShiftVal[p] = *PrevShift;
*PrevShift = NewShift; // Update prev shift
}
for ( ; p < MaxShifts; p++) ShiftVal[p] = *PrevShift; // Fill array to end with final shift value
}
return UsingShift;
}
// ==============================================================
int DecompressFast(BLOCK_DATA *Blk, USHORT NumWords)
{
int i, EncodeCount;
short ShiftVal[NSHIFTS]; // Shift values (one per SHIFTWIN words)
USHORT Method[MAX_DIFF_LOOPS]; // Block processing methods used per iteration
#if DB_BLOCKCHECK // If debug mode block check enabled
ULONG BlockCheck = BioRead(16); // Read block check bits
#endif
bool UsingShift = CheckShift(ShiftVal, NumWords, &Blk->PrevShift, &Blk->PrevUsedShift);
bool UsingBD4 = BioReadFlag(); // See if using BD4
if (UsingBD4)
{
EncodeCount = InputDiff(Blk->BD4PrevEncodeCount);
if (InvalidEncodeCount(EncodeCount, Blk->MaxBD4Loops)) return(GlobalErrorFlag = SFARKLIB_ERR_CORRUPT);
Blk->BD4PrevEncodeCount = EncodeCount;
}
else // Using BD2/3
{
EncodeCount = InputDiff(Blk->PrevEncodeCount);
if (InvalidEncodeCount(EncodeCount, Blk->MaxLoops)) return(GlobalErrorFlag = SFARKLIB_ERR_CORRUPT);
Blk->PrevEncodeCount = EncodeCount;
for(i = 0; i < EncodeCount; i++)
Method[i] = BioReadFlag(); // Read flags for BD2/3
}
// If using LPC, check for and read flags...
ULONG LPCflags;
bool UsingLPC = (Blk->FileHeader.CompMethod != COMPRESSION_v2Fast);
if (UsingLPC)
{
if (BioReadFlag()) // Any flags?
LPCflags = BioRead(16) | (BioRead(16) << 16); // Then read them (32 bits)
else // else
LPCflags = 0;
}
// Read the file and unpack the bitstream into buffer at Buf1p...
if (int UnCrunchResult = UnCrunchWin(Blk->SrcBuf, NumWords, OPTWINSIZE) < 0) // failed?
{
sprintf(MsgTxt, "ERROR - UnCrunchWin returned: %d %s", UnCrunchResult, CorruptedMsg);
msg(MsgTxt, MSG_PopUp);
return(GlobalErrorFlag = SFARKLIB_ERR_CORRUPT);
}
if (UsingLPC)
{
UnLPC(Blk->DstBuf, Blk->SrcBuf, NumWords, Blk->nc, &LPCflags);
AWORD *SwapBuf = Blk->SrcBuf; Blk->SrcBuf = Blk->DstBuf; Blk->DstBuf = SwapBuf;
}
if (UsingBD4)
{
for (i = EncodeCount-1; i >= 0; i--)
{
UnBufDif4(Blk->DstBuf, Blk->SrcBuf, NumWords, &(Blk->PrevIn[i]));
AWORD *SwapBuf = Blk->SrcBuf; Blk->SrcBuf = Blk->DstBuf; Blk->DstBuf = SwapBuf;
}
}
else
{
for (i = EncodeCount-1; i >= 0; i--)
{
switch (Method[i])
{
case 0: UnBufDif2(Blk->DstBuf, Blk->SrcBuf, NumWords, &(Blk->PrevIn[i])); break;
case 1: UnBufDif3(Blk->DstBuf, Blk->SrcBuf, NumWords, &(Blk->PrevIn[i])); break;
}
AWORD *SwapBuf = Blk->SrcBuf; Blk->SrcBuf = Blk->DstBuf; Blk->DstBuf = SwapBuf;
}
}
if (UsingShift) UnBufShift(Blk->SrcBuf, NumWords, ShiftVal);
#if DB_BLOCKCHECK // If debug mode block check enabled
ULONG CalcBlockCheck = adler32(0, (const BYTE *) Blk->SrcBuf, 2*NumWords) & 0xffff;
//ULONG CalcBlockCheck = Blk->FileCheck & 0xffff;
//printf("Audio Block Checks Read: %ld, Calc %ld Length=%d\n", BlockCheck, CalcBlockCheck, 2*NumWords);
//getc(stdin);
if (BlockCheck != CalcBlockCheck) // Compare to calculated cheksum
{
msg("*** Audio Block check FAIL");
}
//else
// printf("Audio Block check Ok\n");
#endif
Blk->FileCheck = 2 * Blk->FileCheck + BufSum(Blk->SrcBuf, NumWords);
return SFARKLIB_SUCCESS;
}
// ==============================================================
int ProcessNextBlock(BLOCK_DATA *Blk)
{
//int TotBytesRead = 0; // Total bytes read in file
int NumWords; //
uint32_t n, m; // NB: Must be 32-bit integer
#define AWBYTES (sizeof(AWORD))
BYTE *zSrcBuf = (BYTE *) Blk->SrcBuf;
BYTE *zDstBuf = (BYTE *) Blk->DstBuf;
switch (Blk->FileSection)
{
case AUDIO:
{
NumWords = Blk->ReadSize; // Number of words we will read in this block
n = NumWords * AWBYTES; // ... and number of bytes
if (Blk->TotBytesWritten + n >= Blk->FileHeader.PostAudioStart) // Short block? (near end of file)
{
n = Blk->FileHeader.PostAudioStart - Blk->TotBytesWritten; // Get exact length in bytes
NumWords = n / AWBYTES; // ... and words
Blk->FileSection = POST_AUDIO; // End of Audio -- PostAudio section is next
}
//printf("AUDIO, read %ld bytes\n", n);
if (Blk->FileHeader.CompMethod == COMPRESSION_v2Turbo) // If using Turbo compression
DecompressTurbo(Blk, NumWords); // Decompress
else // For all other methods
DecompressFast(Blk, NumWords); // Decompress
//printf("B4 WriteOutputFile: %ld\n", adler32(0, (const BYTE *) Blk->SrcBuf, n) & 0xffff);
#ifdef __BIG_ENDIAN__
#define WFIX(I) s = bp[I+0]; bp[I+0] = bp[I+1]; bp[I+1] = s;
BYTE *bp = (BYTE *) Blk->SrcBuf; BYTE *ep = bp + n;
do {
BYTE s;
WFIX(0); WFIX(2); WFIX(4); WFIX(6);
WFIX(8); WFIX(10); WFIX(12); WFIX(14);
bp += 16;
} while (bp < ep);
#undef WFIX
#endif
WriteOutputFile((const BYTE *)Blk->SrcBuf, n); // Write to output file
Blk->TotBytesWritten += n; // Accumulate total bytes written
break;
}
case PRE_AUDIO: case POST_AUDIO: case NON_AUDIO:
{
BioReadBuf((BYTE *) &n, sizeof(n));
FixEndian(&n, sizeof(n));
//printf("Reading PRE/POST AUDIO block, compressed %ld bytes\n", n);
if (n > ZBUF_SIZE) // Check for valid block length
{
sprintf(MsgTxt, "ERROR - Invalid length for Non-audio Block (apparently %d bytes) %s", n, CorruptedMsg);
msg(MsgTxt, MSG_PopUp);
return (GlobalErrorFlag = SFARKLIB_ERR_CORRUPT);
}
BioReadBuf(zSrcBuf, n); // Read the block
m = UnMemcomp(zSrcBuf, n, zDstBuf, ZBUF_SIZE); //printf("PRE/POST AUDIO block, compressed %ld bytes, uncompressed %ld bytes\n", n, m);
// Uncompress
if (GlobalErrorFlag != SFARKLIB_SUCCESS) return(GlobalErrorFlag);
if (m <= ZBUF_SIZE) // Uncompressed ok & size is valid?
{
//printf("writing uncompressed block %ld bytes\n", m);
Blk->FileCheck = adler32(Blk->FileCheck, zDstBuf, m); // Accumulate checksum
WriteOutputFile(zDstBuf, m); // and write to output file
Blk->TotBytesWritten += m; // Accumulate byte count
}
else
return SFARKLIB_ERR_CORRUPT;
#if DB_BLOCKCHECK // If debug mode block check enabled
ULONG BlockCheck = BioRead(16); // Read block check bits
FixEndian(&BlockCheck, sizeof(Blockcheck));
ULONG CalcBlockCheck = adler32(0, zDstBuf, m) & 0xFFFF;
printf("NonAudio Block Checks Read: %ld, Calc %ld Length=%d\n", BlockCheck, CalcBlockCheck, m);
if (BlockCheck != CalcBlockCheck) // Compare to calculated cheksum
{
printf("*** NonAudio Block check FAIL\n");
}
else
printf("NonAudio Block check Ok\n");
#endif
//printf("PRE/POST AUDIO, read %ld bytes, writing %ld bytes...", n, m);
if (Blk->TotBytesWritten >= Blk->FileHeader.OriginalSize) // Have we finished the file?
Blk->FileSection = FINISHED;
else if (Blk->FileSection == PRE_AUDIO && Blk->TotBytesWritten >= Blk->FileHeader.AudioStart) // finished Pre-Audio?
Blk->FileSection = AUDIO; // .. then next section is Audio
break;
} // case
} //switch
//sprintf(MsgTxt, "BytesWritten: %ld of %ld", Blk->TotBytesWritten, Blk->FileHeader.OriginalSize);
//msg(MsgTxt, 0);
return SFARKLIB_SUCCESS;
}
// ==============================================================
int EndProcess(int ErrorNum)
{
CloseInputFile();
CloseOutputFile();
return ErrorNum;
}
// ==============================================================
// Extract License & Notes files
// These are stored as 4-bytes length, followed by length-bytes of compressed data
bool ExtractTextFile(BLOCK_DATA *Blk, ULONG FileType)
{
ULONG n, m;
BYTE *zSrcBuf = (BYTE *) Blk->SrcBuf;
BYTE *zDstBuf = (BYTE *) Blk->DstBuf;
const char *FileExt;
if (FileType == FLAGS_License)
FileExt = LicenseExt;
else if (FileType == FLAGS_Notes)
FileExt = NotesExt;
else
return false;
// NB:Can't use BioReadBuf... aligment problems? Yet it works for ProcessNextBlock() !!??
// Ok, can use ReadInputFile here cause everythjing is whole no. of bytes...
//BioReadBuf((BYTE *)&n, sizeof(n)); // Read length of block from file
ReadInputFile((BYTE *)&n, sizeof(n)); // Read length of block from file
FixEndian(&n, sizeof(n)); // Fix endian
if (n <= 0 || n > ZBUF_SIZE) // Check for valid block length
{
sprintf(MsgTxt, "ERROR - Invalid length for %s file (apparently %ld bytes) %s", FileExt, n, CorruptedMsg);
msg(MsgTxt, MSG_PopUp);
GlobalErrorFlag = SFARKLIB_ERR_CORRUPT;
return false;
}
//BioReadBuf(zSrcBuf, n); // Read the block
ReadInputFile((BYTE *)zSrcBuf, n); // Read the block
m = UnMemcomp(zSrcBuf, n, zDstBuf, ZBUF_SIZE); // Uncompress
Blk->FileCheck = adler32(Blk->FileCheck, zDstBuf, m); // Accumulate checksum
if (GlobalErrorFlag || m > ZBUF_SIZE) // Uncompressed ok & size is valid?
return false;
// Write file - Use original file name plus specified extension for OutFileName...
char OutFileName[MAX_FILENAME];
strncpy(OutFileName, Blk->FileHeader.FileName, sizeof(OutFileName)); // copy output filename
ChangeFileExt(OutFileName, FileExt, sizeof(OutFileName));
OpenOutputFile(OutFileName); // Create notes / license file
WriteOutputFile(zDstBuf, m); // and write to output file
CloseOutputFile();
if (FileType == FLAGS_License)
{
sprintf(MsgTxt, "Created license file: %s", OutFileName);
msg(MsgTxt, 0);
if (GetLicenseAgreement((const char *)zDstBuf, OutFileName) == false)
{
GlobalErrorFlag = SFARKLIB_ERR_LICENSE;
return EndProcess(false);
}
}
else if (FileType == FLAGS_Notes)
{
sprintf(MsgTxt, "Created notes file: %s", OutFileName);
msg(MsgTxt, 0);
DisplayNotes((const char *)zDstBuf, OutFileName);
}
return true;
}
// ==============================================================
int Decode(const char *InFileName, const char *ReqOutFileName)
{
char OutFileName[MAX_FILEPATH]; // File name for current output file
int NumLoops; // Number of loops before screen update etc.
BLOCK_DATA Blk;
memset(&Blk, 0, sizeof(Blk));
V2_FILEHEADER *FileHeader = &Blk.FileHeader;
// NB: We keep 2 buffers with pointers in Blk->SrcBuf and Blk->DstBuf
// Generally we process from SrcBuf to DstBuf then swap the pointers,
// so that current data is always at SrcBuf
// NB: On MacOS/GNU C, the following allocation of Zbuf1 and Zbuf2 causes "segmentation fault"
// BYTE Zbuf1[ZBUF_SIZE]; // Buffer1
// BYTE Zbuf2[ZBUF_SIZE]; // Buffer2
// so instead...
#if 0
static BYTE Zbuf1[ZBUF_SIZE]; // Buffer1
static BYTE Zbuf2[ZBUF_SIZE]; // Buffer2
#else
if (Zbuf1 == NULL) Zbuf1 = (BYTE *) calloc(ZBUF_SIZE, sizeof(BYTE));
if (Zbuf2 == NULL) Zbuf2 = (BYTE *) calloc(ZBUF_SIZE, sizeof(BYTE));
if (Zbuf1 == NULL || Zbuf2 == NULL)
return EndProcess(SFARKLIB_ERR_MALLOC);
#endif
Blk.SrcBuf = (AWORD *) Zbuf1; // Point to Zbuf1
Blk.DstBuf = (AWORD *) Zbuf2; // Point to Zbuf2
// Initialisation...
BioDecompInit(); // Initialise bit i/o
LPCinit(); // Init LPC
GlobalErrorFlag = SFARKLIB_SUCCESS;
// Open input (.sfArk) file and read the header...
OpenInputFile(InFileName);
if (GlobalErrorFlag) return EndProcess(GlobalErrorFlag); // Something went wrong?
ReadHeader(FileHeader, Zbuf1, ZBUF_SIZE);
if (GlobalErrorFlag) return EndProcess(GlobalErrorFlag); // Something went wrong?
if (ReqOutFileName == NULL) // If no output filename requested
ReqOutFileName = FileHeader->FileName;
if ((FileHeader->Flags & FLAGS_License) != 0) // License file exists?
{
if (ExtractTextFile(&Blk, FLAGS_License) == false)
return EndProcess(GlobalErrorFlag);
}
if ((FileHeader->Flags & FLAGS_Notes) != 0) // Notes file exists?
{
if (ExtractTextFile(&Blk, FLAGS_Notes) == false)
return EndProcess(GlobalErrorFlag);
}
// Use original file extension for OutFileName...
strncpy(OutFileName, ReqOutFileName, sizeof(OutFileName)); // Copy output filename
OpenOutputFile(OutFileName); // Create the main output file...
// Set the decompression parameters...
switch (FileHeader->CompMethod) // Depending on compression method that was used...
{
case COMPRESSION_v2Max:
{
//msg("Compression: Max\n");
Blk.ReadSize = 4096;
Blk.MaxLoops = 3;
Blk.MaxBD4Loops = 5;
Blk.nc = 128;
Blk.WinSize = OPTWINSIZE;
NumLoops = 2 * 4096 / Blk.ReadSize;
break;
}
case COMPRESSION_v2Standard:
{
//printf("Compression: Standard\n");
Blk.MaxLoops = 3;
Blk.MaxBD4Loops = 3;
Blk.ReadSize = 4096;
Blk.nc = 8;
Blk.WinSize = OPTWINSIZE;
NumLoops = 50 * 4096 / Blk.ReadSize;
break;
}
case COMPRESSION_v2Fast:
{
//printf("Compression: Fast\n");
Blk.MaxLoops = 20;
Blk.MaxBD4Loops = 20;
Blk.ReadSize = 1024;
Blk.WinSize = OPTWINSIZE;
NumLoops = 300 * 4096 / Blk.ReadSize;
break;
}
case COMPRESSION_v2Turbo:
{
//printf("Compression: Turbo\n");
Blk.MaxLoops = 3;
Blk.MaxBD4Loops = 0;
Blk.ReadSize = 4096;
Blk.WinSize = OPTWINSIZE << 3;
NumLoops = 400 * 4096 / Blk.ReadSize;
break;
}
default:
{
sprintf(MsgTxt, "Unknown Compression Method: %d%s", FileHeader->CompMethod, CorruptedMsg);
GlobalErrorFlag = SFARKLIB_ERR_INCOMPATIBLE;
msg(MsgTxt, MSG_PopUp);
return EndProcess(GlobalErrorFlag);
}
}
// Process the main file...
Blk.FileSection = PRE_AUDIO; // We start with pre-audio data
ULONG ProgressUpdateInterval = Blk.FileHeader.OriginalSize / 100; // Calculate progress update
ULONG NextProgressUpdate = ProgressUpdateInterval;
int ProgressPercent = 0;
UpdateProgress(0);
//int BlockNum = 0;
for (Blk.FileSection = PRE_AUDIO; Blk.FileSection != FINISHED; )
{
for (int BlockCount = 0; BlockCount < NumLoops && Blk.FileSection != FINISHED; BlockCount++)
{
//printf("Block: %d\n", ++BlockNum);
ProcessNextBlock(&Blk);
while (Blk.TotBytesWritten >= NextProgressUpdate) // Further 1% done since last UpdateProgress?
{
UpdateProgress(++ProgressPercent);
NextProgressUpdate += ProgressUpdateInterval;
}
if (GlobalErrorFlag) return EndProcess(GlobalErrorFlag);
}
// CheckForCancel();
if (GlobalErrorFlag) return EndProcess(GlobalErrorFlag);
}
if (ProgressPercent != 100) UpdateProgress(100);
// Check the CheckSum...
if (Blk.FileCheck != FileHeader->FileCheck)
{
sprintf(MsgTxt, "CheckSum Fail!%s",CorruptedMsg);
msg(MsgTxt, MSG_PopUp);
//sprintf(MsgTxt, "Calc check %lx", Blk.FileCheck);
//msg(MsgTxt, MSG_PopUp);
GlobalErrorFlag = SFARKLIB_ERR_FILECHECK;
return EndProcess(GlobalErrorFlag);
}
sprintf(MsgTxt, "Created %s (%ld kb) successfully.", ReqOutFileName, Blk.TotBytesWritten/1024);
msg(MsgTxt, 0);
return EndProcess(GlobalErrorFlag);
}
// ==============================================================
// Adjust integer held at *num for this machine's endian system
void FixEndian(void *num, int nsize)
{
#ifdef __BIG_ENDIAN__
int i;
BYTE bb[4];
for (i = 0; i < nsize; i++) bb[i] = ((BYTE *) num)[i];
for (i = 0; i < nsize; i++) ((BYTE *) num)[i] = bb[nsize-1-i];
#else
#ifndef __LITTLE_ENDIAN__
#error ENDIAN system undefined
#endif
#endif
}
// ==============================================================
|