chovy-sign/PspCrypto/Lzrc.cs

909 lines
26 KiB
C#

using System;
using System.Collections.Generic;
using System.Net.NetworkInformation;
using System.Text;
namespace PspCrypto
{
public class Lzrc
{
private bool np9660;
private byte[] input;
private int in_ptr;
private int in_len;
private byte[] output;
private int out_ptr;
private int out_len;
private uint range;
private uint code;
private uint out_code;
private byte lc;
private byte[][] bm_literal;
private byte[][] bm_dist_bits;
private byte[][] bm_dist;
private byte[][] bm_match;
private byte[][] bm_len;
const int MAX_WIN_SZ = 16384;
const int MAX_TBL_SZ = 65280;
const int TBL_SZ = 65536;
private byte[] text_buf = new byte[TBL_SZ];
private int t_start, t_end, t_fill, sp_fill;
private int t_len, t_pos;
private int[] prev = new int[TBL_SZ], next = new int[TBL_SZ];
private int[] root = new int[TBL_SZ];
public Lzrc(bool np9660 = false)
{
this.np9660 = np9660;
if (np9660)
{
this.bm_literal = new byte[8][];
this.bm_dist_bits = new byte[8][];
this.bm_dist = new byte[18][];
this.bm_match = new byte[8][];
this.bm_len = new byte[8][];
}
else
{
this.bm_literal = new byte[8][];
this.bm_dist_bits = new byte[8][];
this.bm_dist = new byte[16][];
this.bm_match = new byte[8][];
this.bm_len = new byte[8][];
}
}
static void Init(byte[][] arr, byte value, int length)
{
for (int i = 0; i < arr.Length; i++)
{
arr[i] = new byte[length];
for (int j = 0; j < length; j++)
{
arr[i][j] = value;
}
}
}
private byte rc_getbyte()
{
if (in_ptr == in_len)
{
throw new Exception("End of input!");
}
return input[in_ptr++];
}
void rc_putbyte(byte b)
{
if (out_ptr == out_len)
{
throw new Exception("Output overflow!");
}
output[out_ptr++] = b;
}
void re_init(ref byte[] in_buf, int in_len)
{
input = in_buf;
this.in_len = in_len;
in_ptr = 0;
this.output = new byte[in_len];
this.out_len = in_len;
out_ptr = 0;
range = 0xffffffff;
code = 0x00000000;
lc = 5;
out_code = 0xffffffff;
re_putbyte(lc);
if (this.np9660)
{
Init(bm_literal, 0x80, 256);
Init(bm_dist_bits, 0x80, 39);
Init(bm_dist, 0x80, 8);
Init(bm_match, 0x80, 8);
Init(bm_len, 0x80, 31);
}
else
{
Init(bm_literal, 0x80, 256); // 2048 2680 2656
Init(bm_dist_bits, 0x80, 23); // 184
Init(bm_dist, 0x80, 8); // 128
Init(bm_match, 0x80, 8); // 64
Init(bm_len, 0x80, 32); // 256
}
//memset(re->bm_literal, 0x80, 2048);
//memset(re->bm_dist_bits, 0x80, 312);
//memset(re->bm_dist, 0x80, 144);
//memset(re->bm_match, 0x80, 64);
//memset(re->bm_len, 0x80, 248);
}
void rc_init(byte[] out_buf, int out_len, byte[] in_buf, int in_len)
{
input = in_buf;
this.in_len = in_len;
in_ptr = 0;
output = out_buf;
this.out_len = out_len;
out_ptr = 0;
range = 0xffffffff;
lc = rc_getbyte();
code = (uint)((rc_getbyte() << 24) |
(rc_getbyte() << 16) |
(rc_getbyte() << 8) |
rc_getbyte());
out_code = 0xffffffff;
if (this.np9660)
{
Init(bm_literal, 0x80, 256);
Init(bm_dist_bits, 0x80, 39);
Init(bm_dist, 0x80, 8);
Init(bm_match, 0x80, 8);
Init(bm_len, 0x80, 31);
}
else
{
Init(bm_literal, 0x80, 256); // 2048 2680 2656
Init(bm_dist_bits, 0x80, 23); // 184
Init(bm_dist, 0x80, 8); // 128
Init(bm_match, 0x80, 8); // 64
Init(bm_len, 0x80, 32); // 256
}
}
void normalize()
{
if (range < 0x01000000)
{
range <<= 8;
code = (code << 8) + input[in_ptr];
in_ptr++;
}
}
int rc_bit(byte[] probs, int index)
{
uint bound;
normalize();
bound = (range >> 8) * probs[index];
probs[index] -= (byte)(probs[index] >> 3);
if (code < bound)
{
range = bound;
probs[index] += 31;
return 1;
}
else
{
code -= bound;
range -= bound;
return 0;
}
}
int rc_bittree(byte[] probs, int index, int limit)
{
int number = 1;
do
{
number = (number << 1) + rc_bit(probs, index + number);
} while (number < limit);
number -= limit;
return number;
}
int rc_number(byte[] prob, int index, int n)
{
int i, number = 1;
if (n > 3)
{
number = (number << 1) + rc_bit(prob, index + 3);
if (n > 4)
{
number = (number << 1) + rc_bit(prob, index + 3);
}
if (n > 5)
{
normalize();
for (i = 0; i < n - 5; i++)
{
range >>= 1;
number <<= 1;
if (code < range)
{
number += 1;
}
else
{
code -= range;
}
}
}
}
if (n > 0)
{
number = (number << 1) + rc_bit(prob, index + 0);
if (n > 1)
{
number = (number << 1) + rc_bit(prob, index + 1);
if (n > 2)
{
number = (number << 1) + rc_bit(prob, index + 2);
}
}
}
return number;
}
public void init_tree()
{
int i;
for (i = 0; i < TBL_SZ; i++)
{
root[i] = -1;
prev[i] = -1;
next[i] = -1;
}
t_start = 0;
t_end = 0;
t_fill = 0;
sp_fill = 0;
}
void fill_buffer()
{
//void *memcpy(void *dest, const void * src, size_t n)
int content_size, back_size, front_size;
if (sp_fill == in_len)
return;
content_size = (t_fill < t_end) ? (MAX_TBL_SZ + t_fill - t_end) : (t_fill - t_end);
if (content_size >= 509)
return;
if (t_fill < t_start)
{
back_size = t_start - t_fill - 1;
if (sp_fill + back_size > in_len)
back_size = in_len - sp_fill;
Array.ConstrainedCopy(input, sp_fill, text_buf, t_fill, back_size);
// memcpy(text_buf + t_fill, re->input + sp_fill, back_size);
sp_fill += back_size;
t_fill += back_size;
}
else
{
back_size = MAX_TBL_SZ - t_fill;
if (t_start == 0)
back_size -= 1;
if (sp_fill + back_size > in_len)
back_size = in_len - sp_fill;
Array.ConstrainedCopy(input, sp_fill, text_buf, t_fill, back_size);
//memcpy(text_buf + t_fill, re->input + sp_fill, back_size);
sp_fill += back_size;
t_fill += back_size;
front_size = t_start;
if (t_start != 0)
front_size -= 1;
if (sp_fill + front_size > in_len)
front_size = in_len - sp_fill;
Array.ConstrainedCopy(input, sp_fill, text_buf, 0, front_size);
//memcpy(text_buf, re->input + sp_fill, front_size);
sp_fill += front_size;
Array.ConstrainedCopy(text_buf, 255, text_buf, MAX_TBL_SZ, front_size);
//memcpy(text_buf + max_tbl_sz, text_buf, 255);
t_fill += front_size;
if (t_fill >= MAX_TBL_SZ)
t_fill -= MAX_TBL_SZ;
}
}
void remove_node(int p)
{
int t, q;
if (prev[p] == -1)
return;
t = text_buf[p + 0];
t = (t << 8) | text_buf[p + 1];
q = next[p];
if (q != -1)
prev[q] = prev[p];
if (prev[p] == -2)
root[t] = q;
else
next[prev[p]] = q;
prev[p] = -1;
next[p] = -1;
}
int insert_node(int pos, out int match_len, out int match_dist, int do_cmp)
{
//Span<byte> src, win;
int i, t, p;
int content_size;
//src = text_buf[pos..];
//win = text_buf[t_start..];
content_size = (t_fill < pos) ? (MAX_TBL_SZ + t_fill - pos) : (t_fill - pos);
t_len = 1;
t_pos = 0;
match_len = t_len;
match_dist = t_pos;
if (in_ptr >= in_len)
{
match_len = 256;
return 0;
}
if (in_ptr >= (in_len - 1))
return 0;
t = text_buf[pos];
t = (t << 8) | text_buf[pos+1];
if (root[t] == -1)
{
root[t] = pos;
prev[pos] = -2;
next[pos] = -1;
return 0;
}
p = root[t];
root[t] = pos;
prev[pos] = -2;
next[pos] = p;
if (p != -1)
prev[p] = pos;
while (do_cmp == 1 && p != -1)
{
for (i = 0; (i < 255 && i < content_size); i++)
{
if (text_buf[pos+i] != text_buf[p + i])
break;
}
if (i > t_len)
{
t_len = i;
t_pos = pos - p;
}
else if (i == t_len)
{
int mp = pos - p;
if (mp < 0)
mp += MAX_TBL_SZ;
if (mp < t_pos)
{
t_len = i;
t_pos = pos - p;
}
}
if (i == 255)
{
remove_node(p);
break;
}
p = next[p];
}
if (this.np9660)
{
// have we calculated match_dist of 256 when its not the end?
if (t_len == 256 && in_ptr < in_len)
return 1;
}
if (t_pos < 0) throw new Exception("t_pos was < 0 :?"); // TODO: figure out why this happens on np9660.
match_len = t_len;
match_dist = t_pos;
return 1;
}
void update_tree(int length)
{
int i, win_size;
int tmp_len, tmp_pos;
win_size = (t_end >= t_start) ? (t_end - t_start) : (MAX_TBL_SZ + t_end - t_start);
for (i = 0; i < length; i++)
{
if (win_size == MAX_WIN_SZ)
{
remove_node(t_start);
t_start += 1;
if (t_start == MAX_TBL_SZ)
t_start = 0;
}
else
{
win_size += 1;
}
if (i > 0)
{
insert_node(t_end, out tmp_len, out tmp_pos, 0);
}
t_end += 1;
if (t_end >= MAX_TBL_SZ)
t_end -= MAX_TBL_SZ;
}
}
void re_bittree(ref byte[] probs,int index, int limit, int number)
{
int n, tmp, bit;
number += limit;
// Get total bits used by number
tmp = number;
n = 0;
while (tmp > 1)
{
tmp >>= 1;
n++;
}
do
{
tmp = number >> n;
bit = (number >> (n - 1)) & 1;
re_bit(ref probs, index + tmp, bit);
n -= 1;
} while (n > 0);
}
void re_bit(ref byte[] prob, int index, int bit)
{
uint bound;
uint old_r, old_c;
byte old_p;
re_normalize();
old_r = range;
old_c = code;
old_p = prob[index];
var pProb = prob[index];
bound = (range >> 8) * (pProb);
pProb -= (byte)(pProb >> 3);
if (bit != 0)
{
range = bound;
pProb += 31;
}
else
{
code += bound;
if (code < old_c)
out_code += 1;
range -= bound;
}
prob[index] = pProb;
}
void re_normalize()
{
if (range < 0x01000000)
{
if (out_code != 0xffffffff)
{
if (out_code > 255)
{
int p, old_c;
p = out_ptr - 1;
do
{
old_c = output[p];
output[p] += 1;
p -= 1;
} while (old_c == 0xff);
}
re_putbyte((byte)(out_code & 0xff));
}
out_code = (code >> 24) & 0xff;
range <<= 8;
code <<= 8;
}
}
void re_putbyte(byte out_byte)
{
if (out_ptr == out_len)
{
out_len += 0x1000;
Array.Resize(ref output, out_len);
}
output[out_ptr++] = out_byte;
}
byte re_getbyte()
{
if (in_ptr == in_len)
{
throw new Exception("End of input!");
}
return input[in_ptr++];
}
void re_number(ref byte[] prob, int index, int n, int number)
{
int i;
UInt32 old_c;
i = 1;
if (n > 3)
{
re_bit(ref prob, index + 3,(number >> (n - i)) & 1);
i += 1;
if (n > 4)
{
re_bit(ref prob, index + 3, (number >> (n - i)) & 1);
i += 1;
if (n > 5)
{
re_normalize();
for (i = 3; i < n - 2; i++)
{
range >>= 1;
if (((number >> (n - i)) & 1) == 0)
{
old_c = code;
code += range;
if (code < old_c)
out_code += 1;
}
}
}
}
}
if (n > 0)
{
re_bit(ref prob, index + 0, (number >> (n - i - 0)) & 1);
if (n > 1)
{
re_bit(ref prob, index + 1, (number >> (n - i - 1)) & 1);
if (n > 2)
{
re_bit(ref prob, index + 2, (number >> (n - i - 2)) & 1);
}
}
}
}
void re_flush()
{
re_putbyte((byte)((out_code) & 0xff));
re_putbyte((byte)((code >> 24) & 0xff));
re_putbyte((byte)((code >> 16) & 0xff));
re_putbyte((byte)((code >> 8) & 0xff));
re_putbyte((byte)((code >> 0) & 0xff));
}
private byte[] re_trunc()
{
Array.Resize(ref output, out_ptr);
return output;
}
public byte[] lzrc_compress(byte[] in_buf, int in_len)
{
int match_step, re_state, len_state, dist_state;
int i, cur_byte, last_byte;
int match_len, len_bits;
int match_dist, dist_bits, limit;
int round = -1;
// initalize buffers to all 0x80
re_init(ref in_buf, in_len);
// initalize the tree
init_tree();
// initalize variable to 0 ...
re_state = 0;
last_byte = 0;
match_len = 0;
match_dist = 0;
bool flg = false;
while (true)
{
round += 1;
match_step = 0;
fill_buffer();
insert_node(t_end, out match_len, out match_dist, 1);
if (match_len < 256)
{
// condition is different if np9660 vs pops
if (this.np9660)
flg = (match_len < 4 && match_dist > 255);
else
flg = (match_len <= 4 && match_dist > 255);
if(flg) // if (condition)
match_len = 1;
update_tree(match_len);
}
// condition is different if np9660 vs pops
if (this.np9660)
flg = (match_len == 1 || (match_len < 4 && match_dist > 255));
else
flg = (match_len == 1 || (match_len <= 4 && match_dist > 255));
if (flg)
{
re_bit(ref bm_match[re_state], match_step, 0);
if (re_state > 0)
re_state -= 1;
cur_byte = re_getbyte();
re_bittree(ref bm_literal[((last_byte >> lc) & 0x07)], 0, 0x100, cur_byte);
if (!this.np9660 && in_ptr >= in_len)
{
re_normalize();
re_flush();
return re_trunc();
}
}
else
{
re_bit(ref bm_match[re_state], match_step, 1);
// write bitstream length (8 bits, where 0 marks the end of it)
len_bits = 0;
for (i = 1; i < 8; i++)
{
match_step += 1;
if ((match_len - 1) < (1 << i))
break;
re_bit(ref bm_match[re_state], match_step, 1);
len_bits += 1;
}
if (i != 8)
re_bit(ref bm_match[re_state], match_step, 0);
if (len_bits > 0)
{
len_state = ((len_bits - 1) << 2) + ((in_ptr << (len_bits - 1)) & 0x03);
re_number(ref bm_len[re_state], len_state, len_bits, (match_len - 1));
if (this.np9660)
flg = (match_len == 256);
else
flg = (in_ptr >= in_len);
if (flg)
{
re_normalize();
re_flush();
return re_trunc();
}
}
// determine limit ...
dist_state = 0;
limit = 8;
// condition is different if np9660 vs pops
if (this.np9660)
flg = (match_len > 3);
else
flg = (match_len > 4);
if (flg) // if (condition)
{
dist_state += 7;
if (this.np9660)
{
limit = 44;
}
else
{
limit = 16;
}
}
// find total 1s in the match_dist
dist_bits = 0;
if(match_dist > 0) {
while ((match_dist >> dist_bits) != 1)
{
dist_bits += 1;
}
}
else
{
throw new Exception("Match dist is 0.. uhh cant match with yourself..");
}
re_bittree(ref bm_dist_bits[len_bits], dist_state, limit, dist_bits);
if (dist_bits > 0)
re_number(ref bm_dist[dist_bits], 0, dist_bits, match_dist);
in_ptr += match_len;
re_state = 6 + ((in_ptr + 1) & 1);
}
last_byte = input[in_ptr - 1];
}
}
public int lzrc_decompress(byte[] out_buf, int out_len, byte[] in_buf, int in_len)
{
int match_step, rc_state, len_state, dist_state = 0;
int i, bit, cur_byte, last_byte;
int match_len, len_bits;
int match_dist, dist_bits, limit;
int match_src;
int round = -1;
bool flg = false;
len_state = 0;
rc_init(out_buf, out_len, in_buf, in_len);
/*if ((lc & 0x80) != 0)
{
Buffer.BlockCopy(in_buf, 5, out_buf, 0, (int)code);
return (int)code;
}*/
rc_state = 0;
last_byte = 0;
while (true)
{
round += 1;
match_step = 0;
bit = rc_bit(bm_match[rc_state], match_step);
// if bit is 0, just copy from the bit tree into the output ?
if (bit == 0)
{
if (rc_state > 0)
rc_state -= 1;
cur_byte = rc_bittree(bm_literal[(((out_ptr & 7) << 8) + last_byte >> lc) & 0x07], 0, 0x100);
rc_putbyte((byte)cur_byte);
if (out_ptr == out_len) return out_ptr;
}
else // This essentially goes; "hey the bytes that go here, are the same ones that were already *here*
{
// Determine bit length?
len_bits = 0;
for (i = 0; i < 7; i++)
{
match_step += 1;
bit = rc_bit(bm_match[rc_state], match_step);
if (bit == 0)
break;
len_bits += 1;
}
// Get the match length ..
if (len_bits == 0)
{
match_len = 1;
}
else
{
len_state = ((len_bits - 1) << 2) + ((out_ptr << (len_bits - 1)) & 0x03);
match_len = rc_number(bm_len[rc_state], len_state, len_bits);
if (this.np9660 && match_len == 0xFF)
return out_ptr;
}
dist_state = 0;
limit = 8;
if (this.np9660)
flg = (match_len > 2);
else
flg = (match_len > 3);
if (flg)
{
dist_state += 7;
if (this.np9660)
limit = 44;
else
limit = 16;
}
dist_bits = rc_bittree(bm_dist_bits[len_bits], dist_state, limit);
if (dist_bits > 0)
match_dist = rc_number(bm_dist[dist_bits], 0, dist_bits);
else
match_dist = 1;
match_src = out_ptr - match_dist;
if (match_dist > out_ptr || match_dist < 0)
{
//Console.WriteLine($"match_dist out of range! 0x{match_dist:x8}");
//return -1; // test
throw new Exception($"match_dist out of range! 0x{match_dist:x8}");
}
for (i = 0; i < match_len + 1; i++)
{
rc_putbyte(output[match_src++]);
}
rc_state = 6 + ((out_ptr + 1) & 1);
if (out_ptr == out_len) return out_ptr;
}
last_byte = output[out_ptr - 1];
}
}
}
}