/*-
* Copyright (C) 2006 Erik Larsson
*
* All rights reserved.
*
* This program 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 2
* of the License, or (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
package org.catacombae.rarx;
//import org.catacombae.rarx.*;
import java.math.BigInteger;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.util.zip.CRC32;
public class DecompressionCode {
private static final int MAXWINSIZE = 0x100000;
private static final int MAXWINMASK = (MAXWINSIZE-1);
private static final int UNP_MEMORY = MAXWINSIZE;
public static final short NC = 298; /* alphabet = {0,1,2, .,NC - 1} */
public static final short DC = 48;
public static final short RC = 28;
public static final short BC = 19;
public static final short MC = 257;
public static final int CODE_HUFFMAN = 0;
public static final int CODE_LZ = 1;
public static final int CODE_LZ2 = 2;
public static final int CODE_REPEATLZ = 3;
public static final int CODE_CACHELZ = 4;
public static final int CODE_STARTFILE = 5;
public static final int CODE_ENDFILE = 6;
public static final int CODE_ENDMM = 7;
public static final int CODE_STARTMM = 8;
public static final int CODE_MMDELTA = 9;
public static final int LHD_SPLIT_BEFORE = 1; //flag bit 0
public static final int LHD_SPLIT_AFTER = 2; //flag bit 1
public static final int LHD_PASSWORD = 4; //flag bit 2
public static final int LHD_COMMENT = 8; //flag bit 3
public static final int LHD_SOLID = 16; //flag bit 4
public static final int LHD_WINDOWMASK = 0x00e0;
public static final int LHD_WINDOW64 = 0;
public static final int LHD_WINDOW128 = 32;
public static final int LHD_WINDOW256 = 64;
public static final int LHD_WINDOW512 = 96;
public static final int LHD_WINDOW1024 = 128;
public static final int LHD_DIRECTORY = 0x00e0;
public static final int NROUNDS = 32;
/* Static variables belonging to the function Unpack. */
private static final short[] LDecode = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20,
24, 28, 32, 40, 48, 56, 64, 80, 96,112,128,160,192,224}; //unsigned char[14]
private static final byte[] LBits = {0,0,0,0,0,0,0,0,1,1,1,1,2,2, //unsigned char[14]
2,2,3,3,3,3,4,4,4,4,5,5,5,5};
private static final int[] DDecode = {0, 1, 2, 3, 4, 6, 8, 12, 16, 24,
32, 48, 64, 96, 128, 192, 256, 384, 512, 768,
1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576,
32768, 49152, 65536, 98304, 131072,196608,262144,327680,393216,458752,
524288,589824,655360,720896,786432,851968,917504,983040}; //int[48]
private static final byte[] DBits = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
7, 7, 8, 8, 9, 9, 10,10,11,11,12,12,13,13,14,14,
15,15,16,16,16,16,16,16,16,16,16,16,16,16,16,16}; //unsigned char[48]
private static final short[] SDDecode = {0,4,8,16,32,64,128,192}; //unsigned char[8]
private static final byte[] SDBits = {2,2,3,4,5,6,6,6}; //unsigned char[8]
public static final /*UBYTE*/ short[] InitSubstTable = {
215, 19,149, 35, 73,197,192,205,249, 28, 16,119, 48,221, 2, 42
,232, 1,177,233, 14, 88,219, 25,223,195,244, 90, 87,239,153,137
,255,199,147, 70, 92, 66,246, 13,216, 40, 62, 29,217,230, 86, 6
, 71, 24,171,196,101,113,218,123, 93, 91,163,178,202, 67, 44,235
,107,250, 75,234, 49,167,125,211, 83,114,157,144, 32,193,143, 36
,158,124,247,187, 89,214,141, 47,121,228, 61,130,213,194,174,251
, 97,110, 54,229,115, 57,152, 94,105,243,212, 55,209,245, 63, 11
,164,200, 31,156, 81,176,227, 21, 76, 99,139,188,127, 17,248, 51
,207,120,189,210, 8,226, 41, 72,183,203,135,165,166, 60, 98, 7
,122, 38,155,170, 69,172,252,238, 39,134, 59,128,236, 27,240, 80
,131, 3, 85,206,145, 79,154,142,159,220,201,133, 74, 64, 20,129
,224,185,138,103,173,182, 43, 34,254, 82,198,151,231,180, 58, 10
,118, 26,102, 12, 50,132, 22,191,136,111,162,179, 45, 4,148,108
,161, 56, 78,126,242,222, 15,175,146, 23, 33,241,181,190, 77,225
, 0, 46,169,186, 68, 95,237, 65, 53,208,253,168, 9, 18,100, 52
,116,184,160, 96,109, 37, 30,106,140,104,150, 5,204,117,112, 84
}; // UBYTE[256]
public static final /*UDWORD*/ int[] CRCTab = new int[256]; // Initialized in static constructor
static {
// Perform initialization of static structures and variables.
//CRCTab = new int[256];
InitCRC(CRCTab);
}
private CRC32 crc = new CRC32();
public final /*UBYTE*/ short[] SubstTable = new short[256];
private /*unsigned*/ byte PN1, PN2, PN3;
/* Note: temp_output_buffer is allocated when a file is about to be
extracted. It is allocated to NewLhd.UnpSize, which can be quite
large. The assumption that the unpacked file can be stored in
memory is awkward and must immediately be replaced with an output
stream. */
private /* unsigned char* */ byte[] temp_output_buffer; /* extract files to this pointer*/
private /* unsigned long* */ int temp_output_buffer_offset; /* size of temp. extract buffer */
//private /*unsigned int*/ int UnpPtr,WrPtr;
private final /*unsigned short*/ short[] OldKey = new short[4];
private static class Decode {
public int MaxNum; // unsigned int
public final int[] DecodeLen = new int[16]; // unsigned int[16]
public final int[] DecodePos = new int[16]; // unsigned int[16]
public final int[] DecodeNum; // unsigned int[]
public Decode() {
this(2);
}
protected Decode(int decodeNumSize) { DecodeNum = new int[decodeNumSize]; }
}
private static class LitDecode extends Decode {
public LitDecode() { super(NC); }
}
private static class DistDecode extends Decode {
public DistDecode() { super(DC); }
}
private static class RepDecode extends Decode {
public RepDecode() { super(RC); }
}
private static class MultDecode extends Decode {
public MultDecode() { super(MC); }
}
private static class BitDecode extends Decode {
public BitDecode() { super(BC); }
}
private final LitDecode LD = new LitDecode();
private final DistDecode DD = new DistDecode();
private final RepDecode RD = new RepDecode();
private final MultDecode[] MD = { new MultDecode(), new MultDecode(),
new MultDecode(), new MultDecode() }; //new MultDecode[4];
private final BitDecode BD = new BitDecode();
private final MultDecode[] MDPtr = { MD[0], MD[1], MD[2], MD[3] };
/* *****************************
* ** unpack stored RAR files **
* *****************************/
// BOOL UnstoreFile(void)
// {
// if ((long)(*temp_output_buffer_offset=UnpRead(temp_output_buffer,
// NewLhd.UnpSize))==-1)
// {
// debug_log("Read error of stored file!");
// return FALSE;
// }
// return TRUE;
// }
/* ****************************************
* ** RAR decompression code starts here **
* ****************************************/
/* #define statements */
public static class AudioVariables {
public int K1,K2,K3,K4,K5; // Should be short...?
public int D1,D2,D3,D4;
public int LastDelta;
public final int[] Dif = new int[11]; //unsigned
public int ByteCount; //unsigned
public int LastChar;
public void zero() {
K1 = 0;
K2 = 0;
K3 = 0;
K4 = 0;
K5 = 0;
D1 = 0;
D2 = 0;
D3 = 0;
D4 = 0;
LastDelta = 0;
Util.zero(Dif);
ByteCount = 0;
LastChar = 0;
}
}
public AudioVariables[] AudV = { new AudioVariables(), new AudioVariables(),
new AudioVariables(), new AudioVariables() };
/*
#define GetBits() \
BitField = ( ( ( (UDWORD)InBuf[InAddr] << 16 ) | \
( (UWORD) InBuf[InAddr+1] << 8 ) | \
( InBuf[InAddr+2] ) ) \
>> (8-InBit) ) & 0xffff;
*/
private static int GetBits(byte[] inBuf, int inAddr, int inBit) {
return ( ( ( (int) (inBuf[inAddr]&0xFF) << 16 ) |
( (short) (inBuf[inAddr+1]&0xFF) << 8 ) |
( (inBuf[inAddr+2]&0xFF) ) )
>>> (8-unsign(inBit)) ) & 0xffff;
}
/*
#define AddBits(Bits) \
InAddr += ( InBit + (Bits) ) >> 3; \
InBit = ( InBit + (Bits) ) & 7;
*/
/**
* Adds Bits
to the address specifiers.
* InAddr is the high 32 bits of the 35-bit address (?) while InBit is the low 3 bits (?).
* Modifies: InAddr, InBit
* @param Bits the bits to add (interpreted as unsigned int)
*/
private void AddBits(int Bits) {
InAddr += (int)(( unsign(InBit) + unsign(Bits) ) >>> 3); // (InBit + Bits) / 8
InBit = (int)(( unsign(InBit) + unsign(Bits) ) & 7);
}
public static int unsign(byte i) {
return i & 0xFF;
}
public static int unsign(short i) {
return i & 0xFFFF;
}
public static long unsign(int i) {
return i & 0xFFFFFFFFL;
}
public static BigInteger unsign(long i) {
return new BigInteger(1, Util.toByteArrayBE(i));
}
private static class Struct_NewFileHeader {
short HeadCRC;
byte HeadType;
short Flags;
short HeadSize;
int PackSize;
int UnpSize;
byte HostOS;
int FileCRC;
int FileTime;
byte UnpVer;
byte Method;
short NameSize;
int FileAttr;
};
private NewFileHeader NewLhd;// = new NewFileHeader();
private RARFileEntryStream ArcPtr = null;
private int Encryption;
private long UnpPackedSize;
private /*unsigned long*/ int CurUnpRead, CurUnpWrite;
//private byte[] UnpBuf;
private int BitField; //unsigned int
//private int Number; //unsigned int
public final byte[] InBuf = new byte[8192]; /* input read buffer */
public final byte[] UnpOldTable = new byte[MC*4];
public int InAddr,InBit,ReadTop; //unsigned int
//public int LastDist,LastLength; //unsigned int
//private int Length,Distance; //unsigned int
//public final int[] OldDist = new int[4]; //unsigned int
//public int OldDistPtr; //unsigned int
public int UnpAudioBlock;
public int UnpChannels;
public int CurChannel;
public int ChannelDelta;
private boolean FileFound;
/* *** 38.3% of all CPU time is spent within this function!!! */
/**
* Unpacks stuff.
* *
* Global read set (constants): * DBits (final byte[48]) * DDecode (final int[48]) * LBits (final byte[14]) * LDecode (final short[14]) * LHD_SOLID (final int) * MAXWINMASK (final int) * MDPtr (final {@link MultDecode}[4]) * SDBits (final byte[8]) * SDDecode (final short[8]) * * Global read set (variables): * FileFound (boolean) * NewLhd ({@link Struct_NewFileHeader}) * * Global modify set: * BitField (int) * CurChannel (int) * DD (final {@link DistDecode}) * InAddr (int) * InBit (int) * InBuf (final byte[8192]) * LD (final {@link LitDecode}) * temp_output_buffer (byte[]) * temp_output_buffer_offset (int) * UnpAudioBlock (int) * UnpChannels (int) * * Local use set: * Bits (int) * (in) DestUnpSize (long) * Distance (int) * LastDist (int) * LastLength (int) * Length (int) * Number (int) * OldDist (final int[4]) * OldDistPtr (int) * (in) UnpAddr (byte[]) * UnpBuf (byte[]) * UnpPtr (int) * WrPtr (int) * * Call set: * {@link #UnpInitData} * {@link #UnpReadBuf} * {@link #ReadTables} * {@link #debug_log} * {@link System#arraycopy} * {@link #DecodeNumber} * {@link #DecodeAudio} * {@link #GetBits} * {@link #AddBits} * {@link #ReadLastTables} **/ public void Unpack(/*unsigned char*/ byte[] UnpAddr, /*long DestUnpSize, */RARFileEntryStream i_ArcPtr, OutputStream dataOut, NewFileHeader i_NewLhd) throws IOException { // catacombae ArcPtr = i_ArcPtr; NewLhd = i_NewLhd; long DestUnpSize = NewLhd.getUnpSize(); UnpPackedSize = NewLhd.getPackSize(); FileFound = true; //Otherwise no data will be written, just uncompressed and skipped (for the purpose of extracting solid archives). Encryption = 0; CurUnpRead = CurUnpWrite = 0; // /catacombae int Bits; // unsigned int byte[] UnpBuf=UnpAddr; /* UnpAddr is a pointer to the unpack buffer */ int UnpPtr = 0; // Pointer to where in the buffer we are at present int WrPtr = 0; // Pointer to where in the buffer UnpBuf we were at the last write int Length = 0; int Distance = 0; int LastDist = 0; int LastLength = 0; int Number = 0; final int[] OldDist = new int[4]; int OldDistPtr = 0; System.out.println("UnpInitData"); UnpInitData(UnpBuf); System.out.println("UnpReadBuf"); UnpReadBuf(true); if((NewLhd.getFlags() & LHD_SOLID) == 0) { System.out.println("ReadTables"); ReadTables(); } DestUnpSize--; int l256 = 0, g269 = 0, e269 = 0, e256 = 0, l261 = 0, l270 = 0; // debug / understanding while(DestUnpSize>=0) { System.out.println("Looping (DestUnpSize=" + DestUnpSize + ")"); UnpPtr &= MAXWINMASK; if(unsign(InAddr) > InBuf.length-30) UnpReadBuf(false); if(((WrPtr-UnpPtr) & MAXWINMASK)<270 && WrPtr!=UnpPtr) { System.out.println("YO"); if(FileFound) { // Flush extracted data to file if(!writeData(UnpBuf, UnpPtr, WrPtr, dataOut)) DestUnpSize = -1; } WrPtr=UnpPtr; } else { //System.out.println("((" + WrPtr + "-" + UnpPtr + ") & MAXWINMASK) >= 270"); //System.out.println(((WrPtr-UnpPtr) & MAXWINMASK) + "<270"); } if(UnpAudioBlock != 0) { Number = DecodeNumber(MDPtr[CurChannel]); if (Number==256) { ReadTables(); } else { UnpBuf[UnpPtr++]=DecodeAudio(Number); if (++CurChannel==UnpChannels) CurChannel=0; DestUnpSize--; } //continue; } else { Number = DecodeNumber(LD); if(Number<256) { // stored System.out.println(Number); ++l256; UnpBuf[UnpPtr++]=(byte)Number; DestUnpSize--; //continue; } else if(Number>269) { ++g269; Length = LDecode[Number-=270]+3; if ((Bits=LBits[Number])>0) { BitField = GetBits(InBuf, InAddr, InBit); Length+=BitField>>>(16-Bits); AddBits(Bits); } Number = DecodeNumber(DD); Distance = DDecode[Number]+1; if ((Bits=DBits[Number])>0) { BitField = GetBits(InBuf, InAddr, InBit); Distance += BitField >>> (16-Bits); AddBits(Bits); } if (Distance>=0x40000) Length++; if (Distance>=0x2000) Length++; LastDist=OldDist[OldDistPtr++ & 3]=Distance; DestUnpSize-=(LastLength=Length); while (Length-- != 0) { UnpBuf[UnpPtr]=UnpBuf[(UnpPtr-Distance) & MAXWINMASK]; UnpPtr=(UnpPtr+1) & MAXWINMASK; } //continue; } else if(Number==269) { ++e269; ReadTables(); //continue; } else if(Number==256) { ++e256; Length = LastLength; Distance = LastDist; LastDist = OldDist[OldDistPtr++ & 3] = Distance; DestUnpSize -= (LastLength=Length); while(Length-- != 0) { UnpBuf[UnpPtr] = UnpBuf[(UnpPtr-Distance) & MAXWINMASK]; UnpPtr = (UnpPtr+1) & MAXWINMASK; } //continue; } else if(Number<261) { ++l261; Distance=OldDist[(OldDistPtr-(Number-256)) & 3]; Number = DecodeNumber(RD); Length=LDecode[Number]+2; if ((Bits=LBits[Number])>0) { BitField = GetBits(InBuf, InAddr, InBit); Length+=BitField>>>(16-Bits); AddBits(Bits); } if (Distance>=0x40000) Length++; if (Distance>=0x2000) Length++; if (Distance>=0x101) Length++; LastDist=OldDist[OldDistPtr++ & 3]=Distance; DestUnpSize-=(LastLength=Length); while (Length-- != 0) { UnpBuf[UnpPtr]=UnpBuf[(UnpPtr-Distance) & MAXWINMASK]; UnpPtr=(UnpPtr+1) & MAXWINMASK; } //continue; } else if(Number<270) { ++l270; Distance=SDDecode[Number-=261]+1; if ((Bits=SDBits[Number])>0) { BitField = GetBits(InBuf, InAddr, InBit); Distance+=BitField>>>(16-Bits); AddBits(Bits); } Length=2; LastDist=OldDist[OldDistPtr++ & 3]=Distance; DestUnpSize-=(LastLength=Length); while (Length-- != 0) { UnpBuf[UnpPtr]=UnpBuf[(UnpPtr-Distance) & MAXWINMASK]; UnpPtr=(UnpPtr+1) & MAXWINMASK; } //continue; } else debug_log("This message will NEVER be printed. If it is printed anyway, kick the programmer."); } } System.out.println("x < 256: " + l256); System.out.println("x > 269: " + g269); System.out.println("x = 269: " + e269); System.out.println("x = 256: " + e256); System.out.println("x < 261: " + l261); System.out.println("x < 270: " + l270); System.out.println("Total: " + (l256+g269+e269+e256+l261+l270)); System.out.println("LD.DecodeLen: 0x" + Util.toHexStringBE(LD.DecodeLen)); System.out.println("LD.DecodeNum: 0x" + Util.toHexStringBE(LD.DecodeNum)); System.out.println("LD.DecodePos: 0x" + Util.toHexStringBE(LD.DecodePos)); System.out.println("LD.MaxNum: 0x" + Util.toHexStringBE(LD.MaxNum)); ReadLastTables(); if (FileFound) { /* flush buffer */ // Flush extracted data to file if(!writeData(UnpBuf, UnpPtr, WrPtr, dataOut)) DestUnpSize = -1; } WrPtr=UnpPtr; } /** * Writes the data in
UnpBuf
to dataOut
according to some rules.*
* Global read set: * {@link #NewLhd} ({@link NewFileHeader}) * * Global modify set: * *temp_output_buffer (byte[]) * temp_output_buffer_offset (int) * * Local use set: * (in) UnpBuf (byte[]) **/ private boolean writeData(byte[] UnpBuf, int UnpPtr, int WrPtr, OutputStream dataOut) throws IOException { if (UnpPtr
InBuf
from the archive stream. If the flag FirstBuf
* is set, the method preserves the last 32 bytes of InBuf and copies them to the front
* of the buffer, filling the buffer with only InBuf.length-32
bytes.*
* Global modify set: * ReadTop (int) * InAddr (int) * Through UnpRead: * UnpPackedSize (int) * CurUnpRead (int) * * Global read set: * InBuf (byte[]) (contents modified in UnpRead) * * Local use set: * (in) FirstBuf (boolean) * RetCode (int) * * Call set: * {@link UnpRead} ** @param FirstBuf flag indicating whether or not we will treat this read as the first read * to the buffer, thus destroying any previous content */ private void UnpReadBuf(boolean FirstBuf) { int RetCode; if(FirstBuf) { ReadTop = UnpRead(InBuf, 0, InBuf.length); InAddr = 0; } else { System.arraycopy(InBuf, InBuf.length-32, InBuf, 0, 32); InAddr &= 0x1f; // discard all but the five least significant bytes... is this modulo 32? think so. RetCode = UnpRead(InBuf, 32, InBuf.length-32); if(RetCode > 0) ReadTop=RetCode+32; else ReadTop=InAddr; } } /** * Reads
Count
bytes from the stream ArcPtr
into
* the buffer Addr
at position offset
. If the
* data is encrypted, it is automatically decrypted. (The variable
* Encryption
tells the function whether is shall consider
* the data to be encrypted.)*
* Global read set: * ArcPtr ({@link java.io.InputStream}) * Encryption (int) * * Global modify set: * UnpPackedSize (int) * CurUnpRead (int) * * Local use set: * (in) Addr (byte[]) * (in) offset (int) * (in) Count (int) * RetCode (int) * I (int) * ReadSize (int) * TotalRead (int) * ReadAddr (byte[]) * readAddrPointer (int) * * Call set: * tread(File, byte[], int, int) * debug_log(String) * DecryptBlock(byte[], int, int) ** @return the number of bytes read, or -1 if an error occurred. */ private /*unsigned int*/ int UnpRead(/*unsigned char **/ byte[] Addr, int offset, /*unsigned int*/ int Count) { int RetCode=0; /*unsigned int*/ int I,ReadSize,TotalRead=0; /*unsigned char **/ byte[] ReadAddr; int readAddrPointer = offset; // catacombae ReadAddr=Addr; while(Count > 0) { ReadSize=(/*unsigned int*/ int)((Count>(/*unsigned long*/ int)UnpPackedSize) ? UnpPackedSize : Count); if (ArcPtr==null) return(0); RetCode=tread(ArcPtr,ReadAddr,readAddrPointer,ReadSize); debug_log("Read " + RetCode + " from file."); CurUnpRead+=RetCode; readAddrPointer+=RetCode; TotalRead+=RetCode; Count-=RetCode; UnpPackedSize-=RetCode; break; // Why the while-loop? if would work just as well. } if (RetCode!= -1) { RetCode=TotalRead; if (Encryption != 0) { if (Encryption<20) { debug_log("Old Crypt() not supported!"); } else { for (I=0;I<(/*unsigned int*/ short)RetCode;I+=16) DecryptBlock(/*&Addr[I]*/Addr, I); } } } return(RetCode); } /** * Reads and initializes the decompression tables.
*
* Global read set (constants): * BC (final short) * DC (final short) * MC (final short) * NC (final short) * RC (final short) * * Global read set (variables): * * Global modify set: * BD (BitDecode) * BitField (int) * CurChannel (int) * DD (DistDecode) * InAddr (int) * InBit (int) (through AddBits) * InBuf (final byte[8192]) * MDPtr (final MultDecode[4]) * RD (RepDecode) * UnpAudioBlock (int) * UnpChannels (int) * UnpOldTable (final byte[MC*4]) * * * Local use set: * BitLength (final byte[BC]) * Table (final byte[MC*4]) * TableSize (int) * N (int) * I (int) * * Call set: * UnpReadBuf * GetBits * Util.zero * AddBits * MakeDecodeTables * DecodeNumber * System.arraycopy **/ private void ReadTables() { System.out.println("ReadTables():"); final /*UBYTE*/ byte[] BitLength = new byte[BC]; final /*unsigned char*/ byte[] Table = new byte[MC*4]; /*int*/ int TableSize,N,I; if(InAddr>InBuf.length-25) { System.out.println("InAddr == " + InAddr); UnpReadBuf(false); } BitField = GetBits(InBuf, InAddr, InBit); UnpAudioBlock = (BitField & 0x8000); if((BitField & 0x4000) == 0) Util.zero(UnpOldTable); AddBits(2); if(UnpAudioBlock != 0) { UnpChannels=((BitField >>> 12) & 3)+1; debug_log("WARNING: UnpChannels = " + UnpChannels); if (CurChannel>=UnpChannels) CurChannel=0; AddBits(2); TableSize=(short)(MC*UnpChannels); } else TableSize=NC+DC+RC; for (I=0;I
Decode
object Dec
supplied as parameter
* according to the data in LenTab
. Initializes Dec
for
* further use in decompression.*
* Global modify set: **/ private void MakeDecodeTables(/*unsigned char **/ byte[] LenTab, Decode Dec, int offset, int Size) { final /*int*/int[] LenCount = new int[16]; final /*int*/int[] TmpPos = new int[16]; /*int*/int I; /*long*/int M, N; //memset(LenCount,0,sizeof(LenCount)); // Java does this automatically for(I=offset; I* * Global read set: * * * Local use set: * (in) LenTab (byte[]) * (i/o) Dec ({@link Decode}) * (in) offset (int) * (in) Size (int) * LenCount (final int[16]) * TmpPos (final int[16]) * I (int) * M (int) * N (int) *
*
* Global modify set: * BitField (int) * In AddBits: * InAddr (int) * InBit (int) * * Local use set: * (in) Deco ({@link Decode}) * I (int) * N (int) * * Call set: * GetBits * AddBits ** * @return the decoded number...? */ private int DecodeNumber(Decode Deco) { /*unsigned int*/ int I; /*register unsigned int*/ int N; System.out.println("GetBits(" + InBuf + ", " + InAddr + ", " + InBit + ");"); BitField = GetBits(InBuf, InAddr, InBit); N=(BitField & 0xFFFE); System.out.println("(1) N == " + N + " (BitField == " + BitField + ")"); if(N
InAddr
and InBit
to 0.ChannelDelta
and CurChannel
to 0.AudioVariables
objects in AudV
through {@link AudioVariables#zero}.unpBuf
and UnpOldTable
.*
* Global read set: * NewLhd ({@link Struct_NewFileHeader}) * LHD_SOLID (final int) * MAXWINSIZE (final int) * * Global modify set: * InAddr (int) * InBit (int) * ChannelDelta (int) * CurChannel (int) * AudV ({@link AudioVariables}) * * Local use set: * (in) unpBuf (byte[]) **/ private void UnpInitData(byte[] unpBuf) { InAddr=InBit=0; if(!((NewLhd.getFlags() & LHD_SOLID) != 0)) { System.out.println("1"); ChannelDelta=CurChannel=0; //memset(AudV,0,sizeof(AudV)); System.out.println("2"); for(AudioVariables av : AudV) av.zero(); //memset(OldDist,0,sizeof(OldDist)); //Util.zero(OldDist); //OldDistPtr=0; //LastDist=LastLength=0; //memset(UnpBuf,0,MAXWINSIZE); System.out.println("3"); Util.zero(unpBuf, 0, MAXWINSIZE); //memset(UnpOldTable,0,sizeof(UnpOldTable)); System.out.println("4"); Util.zero(UnpOldTable); //UnpPtr=WrPtr=0; } } /** * Does some kind of audio decoding that I'm not familiar with conceptually.
*
* Global read set: * CurChannel (int) * * Global modify set: * AudV ({@link AudioVariables}[]) * ChannelDelta (int) * * Local use set: * (in) Delta (int) * V ({@link AudioVariables}) * Ch (int) * NumMinDif (int) * MinDif (int) * PCh (int) * I (int) **/ private /*UBYTE*/ byte DecodeAudio(int Delta) { AudioVariables V; /*unsigned */int Ch; /*unsigned */int NumMinDif,MinDif; int PCh,I; V=AudV[CurChannel]; V.ByteCount++; V.D4=V.D3; V.D3=V.D2; V.D2=V.LastDelta-V.D1; V.D1=V.LastDelta; PCh=8*V.LastChar+V.K1*V.D1+V.K2*V.D2+ V.K3*V.D3+V.K4*V.D4+V.K5*ChannelDelta; PCh=(PCh>>3) & 0xFF; Ch=PCh-Delta; I=((/*signed char*/byte)Delta)<<3; V.Dif[0]+=Math.abs(I); V.Dif[1]+=Math.abs(I-V.D1); V.Dif[2]+=Math.abs(I+V.D1); V.Dif[3]+=Math.abs(I-V.D2); V.Dif[4]+=Math.abs(I+V.D2); V.Dif[5]+=Math.abs(I-V.D3); V.Dif[6]+=Math.abs(I+V.D3); V.Dif[7]+=Math.abs(I-V.D4); V.Dif[8]+=Math.abs(I+V.D4); V.Dif[9]+=Math.abs(I-ChannelDelta); V.Dif[10]+=Math.abs(I+ChannelDelta); ChannelDelta=V.LastDelta=(/*signed char*/byte)(Ch-V.LastChar); V.LastChar=Ch; if((V.ByteCount & 0x1F)==0) { MinDif=V.Dif[0]; NumMinDif=0; V.Dif[0]=0; for (I=1;(/*unsigned */int)I
Buf
at position offset
.
* The decrypted data is stored at the same place in Buf
, thus overwriting the
* encrypted contents.*
* Global read set: * * Global modify set: * In UpdKeys: * Key (final int[4]) * * Local use set: * (in) Buf (byte[]) * (in) offset (int) * InBuf (byte[]) * A (int) * B (int) * C (int) * D (int) * T (int) * TA (int) * TB (int) **/ public void DecryptBlock(/*UBYTE*/byte[] Buf, int offset) { //int I; final int n = offset; byte[] /*UBYTE*/ InBuf = new byte[16]; int /*UDWORD*/ A,B,C,D,T,TA,TB; System.arraycopy(Buf, n, InBuf, 0, InBuf.length); // memcpy(InBuf,Buf,sizeof(InBuf)); // Swap all bytes since data is stored in little endian format. // if(true) { A = ((0xFF & Buf[n+0] ) | ((0xFF & Buf[n+1] )<<8) | ((0xFF & Buf[n+2] )<<16) | ((0xFF & Buf[n+3] )<<24))^Key[0]; B = ((0xFF & Buf[n+4] ) | ((0xFF & Buf[n+5] )<<8) | ((0xFF & Buf[n+6] )<<16) | ((0xFF & Buf[n+7] )<<24))^Key[1]; C = ((0xFF & Buf[n+8] ) | ((0xFF & Buf[n+9] )<<8) | ((0xFF & Buf[n+10])<<16) | ((0xFF & Buf[n+11])<<24))^Key[2]; D = ((0xFF & Buf[n+12]) | ((0xFF & Buf[n+13])<<8) | ((0xFF & Buf[n+14])<<16) | ((0xFF & Buf[n+15])<<24))^Key[3]; // } // else { // The above code should yield the same result as these lines. Test this assumption. int A2 = Util.readIntLE(Buf, n+0 )^Key[0]; int B2 = Util.readIntLE(Buf, n+4 )^Key[1]; int C2 = Util.readIntLE(Buf, n+8 )^Key[2]; int D2 = Util.readIntLE(Buf, n+12)^Key[3]; // } if(A != A2 || B != B2 || C != C2 || D != D2) { System.out.println("Assumption broken!"); System.out.println(" A: 0x" + Util.toHexStringBE(A) + " A2: 0x" + Util.toHexStringBE(A2)); System.out.println(" B: 0x" + Util.toHexStringBE(B) + " B2: 0x" + Util.toHexStringBE(B2)); System.out.println(" C: 0x" + Util.toHexStringBE(C) + " C2: 0x" + Util.toHexStringBE(C2)); System.out.println(" D: 0x" + Util.toHexStringBE(D) + " D2: 0x" + Util.toHexStringBE(D2)); } else System.out.println("Assumption correct!"); for(int I=NROUNDS-1; I>=0; I--) { T=((C+rol(D,11))^Key[I&3]); TA=A^substLong(T); T=((D^rol(C,17))+Key[I&3]); TB=B^substLong(T); A=C; B=D; C=TA; D=TB; } if(true) { C ^= Key[0]; Buf[n+0 ]=(byte)(0xFF & C); Buf[n+1 ]=(byte)(0xFF & (C>>>8)); Buf[n+2 ]=(byte)(0xFF & (C>>>16)); Buf[n+3 ]=(byte)(0xFF & (C>>>24)); D ^= Key[1]; Buf[n+4 ]=(byte)(0xFF & D); Buf[n+5 ]=(byte)(0xFF & (D>>>8)); Buf[n+6 ]=(byte)(0xFF & (D>>>16)); Buf[n+7 ]=(byte)(0xFF & (D>>>24)); A ^= Key[2]; Buf[n+8 ]=(byte)(0xFF & A); Buf[n+9 ]=(byte)(0xFF & (A>>>8)); Buf[n+10]=(byte)(0xFF & (A>>>16)); Buf[n+11]=(byte)(0xFF & (A>>>24)); B ^= Key[3]; Buf[n+12]=(byte)(0xFF & B); Buf[n+13]=(byte)(0xFF & (B>>>8)); Buf[n+14]=(byte)(0xFF & (B>>>16)); Buf[n+15]=(byte)(0xFF & (B>>>24)); } else { System.arraycopy(Util.toByteArrayLE(C^Key[0]), 0, Buf, n+0 , 4); System.arraycopy(Util.toByteArrayLE(D^Key[1]), 0, Buf, n+4 , 4); System.arraycopy(Util.toByteArrayLE(A^Key[2]), 0, Buf, n+8 , 4); System.arraycopy(Util.toByteArrayLE(B^Key[3]), 0, Buf, n+12, 4); } UpdKeys(InBuf); } /* * As we can't use unsigned data types in Java, we'll have to unsign the data * every time we use it. Modifications: unsign the usage of Buf[I] by "& 0xFF". */ /** * Updates keys. ;) *
* Global modify set: * Key (int[4]) * Global read set: * CRCTab (int[256]) * Local use set: * (in) Buf (byte[]) * I (int) **/ public void UpdKeys(byte[]/*UBYTE*/ Buf) { for(int I=0; I<16; I+=4) { Key[0]^=CRCTab[Buf[I] & 0xFF]; /* xxx may be I'll rewrite this */ Key[1]^=CRCTab[Buf[I+1] & 0xFF]; /* in asm for speedup */ Key[2]^=CRCTab[Buf[I+2] & 0xFF]; Key[3]^=CRCTab[Buf[I+3] & 0xFF]; } } /* Password is supposed to be trimmed to the actual password length and not zero-terminated. */ /** * Sets crypt keys. ;)
Password
is supposed to be trimmed to the actual
* password length and not zero-terminated.*
* Global read set: * InitSubstTable (final short[256]) * CRCTab * * Global modify set: * Key (final int[4]) * SubstTable (final short[256]) * In SetOldKeys: * OldKey (final short[4]) * PN1 (byte) * PN2 (byte) * PN3 (byte) * * Local use set: * Password (byte[]) * I (int) * J (int) * K (int) * N1 (short) * N2 (short) * Psw (final byte[256]) * Ch (short) * * Call set: * SetOldKeys * System.arraycopy * EncryptBlock **/ public void SetCryptKeys(byte[] Password) { /*unsigned int*/ int I,J,K, PswLength; /*unsigned char*/ short N1,N2; final /*unsigned char*/ byte[] Psw = new byte[256]; /*UBYTE*/ short Ch; SetOldKeys(Password); Key[0]=0xD3A3B879; // Removed the L at the end (indicates 32 bits in C, 64 in Java) Key[1]=0x3F6D12F7; // -||- Key[2]=0x7515A235; // -||- Key[3]=0xA4E7F123; // -||- //memset(Psw,0,sizeof(Psw)); // Arrays are automatically initialized to 0 in Java. System.arraycopy(Password, 0, Psw, 0, (Password.length
crcTab
, which has to have a length of 256 elements.
* @param crcTab the array where the initialized CRC table is to be stored
* @return the same array that was given as input parameter (for convenience)
*/
static int[] InitCRC(int[] crcTab) {
int I, J;
/*UDWORD*/int C;
for (I=0;I<256;I++) {
for (C=I,J=0;J<8;J++)
C=(C & 1)!=0 ? (C>>>1)^0xEDB88320 : (C>>>1);
crcTab[I]=C;
}
return crcTab;
}
/**
* Calculates the CRC32 checksum from its arguments.
* * Global read set: * CRCTab ([256]) * * Local use set: * (in) StartCRC (int) * (in) Addr (byte[]) * (in) offset (int) * (in) Size (int) * I (int) * * Purely functional behavior, no side effects. ** @return the updated sum */ static int CalcCRC32(int StartCRC, byte[] Addr, int offset, int Size) { /*unsigned */int I; for (I=offset; I