1 module compy.common;
2 
3 import compy.hal;
4 import compy.nintendo;
5 
6 import std.algorithm;
7 import std.format;
8 
9 ulong parseOffset(string arg) {
10 	ulong offset;
11 	if ((arg.length > 1) && (arg[1] == 'x')) {
12 		formattedRead(arg, "0x%x", &offset);
13 	} else {
14 		formattedRead(arg, "%s", &offset);
15 	}
16 	return offset;
17 }
18 
19 enum Format {
20 	HALLZ1,
21 	HALLZ2,
22 	NintendoLZ1,
23 	NintendoLZ2
24 }
25 
26 ubyte[] decomp(Format format, const ubyte[] input) @safe {
27 	size_t unused;
28 	return decomp(format, input, unused);
29 }
30 ubyte[] decomp(Format format, const ubyte[] input, out size_t compressedSize) @safe {
31 	final switch (format) {
32 		case Format.HALLZ1: return HALLZ1.decomp(input, compressedSize);
33 		case Format.HALLZ2: return HALLZ2.decomp(input, compressedSize);
34 		case Format.NintendoLZ1: return NintendoLZ1.decomp(input, compressedSize);
35 		case Format.NintendoLZ2: return NintendoLZ2.decomp(input, compressedSize);
36 	}
37 }
38 
39 ubyte[] comp(Format format, const ubyte[] input) @safe {
40 	final switch (format) {
41 		case Format.HALLZ1: return HALLZ1.comp(input);
42 		case Format.HALLZ2: return HALLZ2.comp(input);
43 		case Format.NintendoLZ1: return NintendoLZ1.comp(input);
44 		case Format.NintendoLZ2: return NintendoLZ2.comp(input);
45 	}
46 }
47 
48 package:
49 
50 ubyte readByte(T)(ref T file) {
51 	import std.range : front, popFront;
52 	auto v = file.front;
53 	file.popFront();
54 	return v;
55 }
56 
57 ubyte[] uncompdata(const ubyte[] input, out ushort size) @safe {
58 	size = cast(ushort)min(input.length,1024);
59 	return buildCommand(0, size, input[0..size]);
60 }
61 
62 const(ubyte)[] increaseval(ubyte input, in int length) pure nothrow @safe {
63 	ubyte[] output = new ubyte[](length);
64 	foreach(ref val; output)
65 		val = input++;
66 	return output;
67 }
68 unittest {
69 	assert((0).increaseval(1) == [0], "Increaseval: First value unaltered");
70 	assert((0).increaseval(3) == [0, 1, 2], "Increaseval: algorithm");
71 	assert((255).increaseval(3) == [255, 0, 1], "Increaseval: Wrapping values");
72 	assert((0).increaseval(0) == [], "Increaseval: Void");
73 }
74 ubyte reversebits(ubyte input) @safe pure nothrow @nogc {
75 	ubyte val = input;
76 	val = ((val >> 1) & 0x55) | ((val << 1) & 0xAA);
77 	val = ((val >> 2) & 0x33) | ((val << 2) & 0xCC);
78 	val = ((val >> 4) & 0x0F) | ((val << 4) & 0xF0);
79 	return val;
80 }
81 ushort reversebits(ushort input) @safe pure nothrow @nogc {
82 	ushort val = input;
83 	val = ((val & 0x5555) << 1) | ((val & 0xAAAA) >> 1);
84 	val = ((val & 0x3333) << 2) | ((val & 0xCCCC) >> 2);
85 	val = ((val & 0x0F0F) << 4) | ((val & 0xF0F0) >> 4);
86 	val = ((val & 0x00FF) << 8) | ((val & 0xFF00) >> 8);
87 	return val;
88 }
89 uint reversebits(uint input) @safe pure nothrow @nogc {
90 	uint val = input;
91 	val = ((val & 0x55555555) << 1) | ((val & 0xAAAAAAAA) >> 1);
92 	val = ((val & 0x33333333) << 2) | ((val & 0xCCCCCCCC) >> 2);
93 	val = ((val & 0x0F0F0F0F) << 4) | ((val & 0xF0F0F0F0) >> 4);
94 	val = ((val & 0x00FF00FF) << 8) | ((val & 0xFF00FF00) >> 8);
95 	val = ((val & 0x0000FFFF) << 16) | ((val & 0xFFFF0000) >> 16);
96 	return val;
97 }
98 pure nothrow @nogc @safe unittest {
99 	assert(cast(ubyte)1.reversebits == cast(ubyte)128, "Bit reversal: algorithm");
100 	assert(cast(uint)0x80000000.reversebits == 1, "Bit reversal: int");
101 }
102 ubyte[] repeatByte(const ubyte[] input, const ubyte[] buffer, out ushort size) @safe pure nothrow {
103 	ubyte match = input[0];
104 	foreach (val; input) {
105 		if ((val != match) || (size == 1024))
106 			break;
107 		size++;
108 	}
109 	return buildCommand(1, size, match);
110 }
111 ubyte[] repeatWord(const ubyte[] input, const ubyte[] buffer, out ushort size) @safe pure nothrow {
112 	if (input.length < 2)
113 		return [];
114 	const(ubyte)[] match = input[0..2];
115 	foreach (k, val; input) {
116 		if ((val != match[k%2]) || (size == 1024))
117 			break;
118 		if (k%2 == 1)
119 			size++;
120 	}
121 	size *= 2;
122 	return buildCommand(2,size/2, match);
123 }
124 ubyte[] incByteFill(const ubyte[] input, const ubyte[] buffer, out ushort size) @safe pure nothrow {
125 	ubyte initialVal, tmpVal;
126 	initialVal = tmpVal = input[0];
127 	size = 1;
128 	foreach (k, val; input[1..$]) {
129 		if (++tmpVal != val)
130 			break;
131 		size++;
132 	}
133 	return buildCommand(3,size,initialVal);
134 }
135 ubyte[] zeroFill(const ubyte[] input, const ubyte[] buffer, out ushort size) @safe pure nothrow {
136 	size = 0;
137 	foreach (k, val; input[1..$]) {
138 		if (val != 0) {
139 			break;
140 		}
141 		size++;
142 	}
143 	return buildCommand(3, size, []);
144 }
145 ubyte[] bufferCopy(const ubyte[] input, const ubyte[] buffer, out ushort size) @safe pure nothrow {
146 	import std.range : empty;
147 	if (input.empty || buffer.empty)
148 		return [];
149 	size = cast(ushort)min(input.length, 1024);
150 	long tmp = -1;
151 	while ((tmp == -1) && (size > 0)) {
152 		tmp = countUntil(buffer, input[0..size--]);
153 	}
154 	return buildCommand(4, size, cast(ushort)tmp);
155 }
156 ubyte[] bufferCopyBigEndian(const ubyte[] input, const ubyte[] buffer, out ushort size) @safe pure nothrow {
157 	import std.range : empty;
158 	if (input.empty || buffer.empty)
159 		return [];
160 	size = cast(ushort)min(input.length, 1024);
161 	long tmp = -1;
162 	while ((tmp == -1) && (size > 0)) {
163 		tmp = countUntil(buffer, input[0..size--]);
164 	}
165 	return buildCommandBE(4, size, cast(ushort)tmp);
166 }
167 ubyte[] bitReverseBufferCopy(const ubyte[] input, const ubyte[] buffer, out ushort size) @safe pure nothrow {
168 	return buildCommand(5, 0, []);
169 }
170 ubyte[] byteReverseBufferCopy(const ubyte[] input, const ubyte[] buffer, out ushort size) @safe pure nothrow {
171 	return buildCommand(6, 0, []);
172 }
173 ubyte[] buildCommand(ubyte ID, ushort length, ubyte payLoad) @safe pure nothrow {
174 	return buildCommand(ID,length,[payLoad]);
175 }
176 ubyte[] buildCommand(ubyte ID, ushort length, ushort payLoad) @safe pure nothrow {
177 	return buildCommand(ID,length,[payLoad&0xFF, payLoad>>8]);
178 }
179 ubyte[] buildCommandBE(ubyte ID, ushort length, ushort payLoad) @safe pure nothrow {
180 	return buildCommand(ID,length,[payLoad>>8, payLoad&0xFF]);
181 }
182 ubyte[] buildCommand(ubyte ID, ushort length, const ubyte[] payLoad) @safe pure nothrow {
183 	ubyte[] output;
184 	if (length == 0) {
185 		return [];
186 	} else if (length <= 32) {
187 		output = new ubyte[payLoad.length+1];
188 		output[0] = cast(ubyte)((ID<<5) + ((length-1)&0x1F));
189 	} else {
190 		output = new ubyte[payLoad.length+2];
191 		output[0] =  cast(ubyte)(0xE0 + (ID<<2) + ((length-1)>>8));
192 		output[1] = (length-1)&0xFF;
193 	}
194 	output[$-payLoad.length..$] = payLoad;
195 	return output;
196 }