Unpletter initial implementation.
This commit is contained in:
commit
9378eadf71
66
.clang-format
Normal file
66
.clang-format
Normal file
@ -0,0 +1,66 @@
|
||||
# Generated from CLion C/C++ Code Style settings
|
||||
BasedOnStyle: LLVM
|
||||
AccessModifierOffset: -4
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignConsecutiveAssignments: None
|
||||
AlignOperands: Align
|
||||
AllowAllArgumentsOnNextLine: false
|
||||
AllowAllConstructorInitializersOnNextLine: false
|
||||
AllowAllParametersOfDeclarationOnNextLine: false
|
||||
AllowShortBlocksOnASingleLine: Always
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: All
|
||||
AllowShortIfStatementsOnASingleLine: Never
|
||||
AllowShortLambdasOnASingleLine: All
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
BreakBeforeBraces: Custom
|
||||
BraceWrapping:
|
||||
AfterCaseLabel: false
|
||||
AfterClass: true
|
||||
AfterControlStatement: Always
|
||||
AfterEnum: true
|
||||
AfterFunction: true
|
||||
AfterNamespace: true
|
||||
AfterUnion: true
|
||||
BeforeCatch: true
|
||||
BeforeElse: true
|
||||
IndentBraces: false
|
||||
SplitEmptyFunction: false
|
||||
SplitEmptyRecord: true
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeTernaryOperators: true
|
||||
BreakConstructorInitializers: BeforeColon
|
||||
BreakInheritanceList: BeforeColon
|
||||
ColumnLimit: 0
|
||||
CompactNamespaces: false
|
||||
ContinuationIndentWidth: 8
|
||||
IndentCaseLabels: true
|
||||
IndentPPDirectives: None
|
||||
IndentWidth: 4
|
||||
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||
MaxEmptyLinesToKeep: 1
|
||||
NamespaceIndentation: All
|
||||
ObjCSpaceAfterProperty: false
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
PointerAlignment: Left
|
||||
ReflowComments: false
|
||||
SpaceAfterCStyleCast: true
|
||||
SpaceAfterLogicalNot: false
|
||||
SpaceAfterTemplateKeyword: false
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeCpp11BracedList: false
|
||||
SpaceBeforeCtorInitializerColon: true
|
||||
SpaceBeforeInheritanceColon: true
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceBeforeRangeBasedForLoopColon: false
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: 0
|
||||
SpacesInAngles: false
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInContainerLiterals: false
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
TabWidth: 4
|
||||
UseTab: Never
|
8
CMakeLists.txt
Normal file
8
CMakeLists.txt
Normal file
@ -0,0 +1,8 @@
|
||||
cmake_minimum_required(VERSION 3.27)
|
||||
project(pletter_uncompress CXX C)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
|
||||
add_executable(pletter pletter.cpp)
|
||||
add_executable(unpletter unpletter/unpletter.cpp)
|
||||
add_executable(unpletter_c unpletter/unpletter_c.c)
|
14
license.txt
Normal file
14
license.txt
Normal file
@ -0,0 +1,14 @@
|
||||
Pletter v0.5b - XL2S Entertainment 2008
|
||||
|
||||
Have fun with it, just do not harm anyone :)
|
||||
|
||||
Keep in mind pletter is based on software with the following license:
|
||||
|
||||
|
||||
Copyright (c) 2002-2003 Team Bomba
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
413
pletter.cpp
Normal file
413
pletter.cpp
Normal file
@ -0,0 +1,413 @@
|
||||
/*
|
||||
Pletter v0.5c1
|
||||
|
||||
XL2S Entertainment
|
||||
*/
|
||||
|
||||
#pragma warning(disable: 4996)
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
using namespace std;
|
||||
|
||||
unsigned char *inData;
|
||||
|
||||
unsigned maxlen[7] = { 128, 128+128, 512+128, 1024+128, 2048+128, 4096+128, 8192+128 };
|
||||
unsigned varcost[65536];
|
||||
|
||||
struct metadata
|
||||
{
|
||||
unsigned reeks;
|
||||
unsigned cpos[7], clen[7];
|
||||
} *meta;
|
||||
|
||||
struct pakdata
|
||||
{
|
||||
unsigned cost, mode, mlen;
|
||||
} *p[7];
|
||||
|
||||
string sourcefilename, destfilename;
|
||||
bool savelength = false;
|
||||
unsigned inLength, offset;
|
||||
|
||||
struct saves
|
||||
{
|
||||
unsigned char *outData;
|
||||
int ep, dp, p, e;
|
||||
void init()
|
||||
{
|
||||
ep = dp = p = e = 0;
|
||||
outData = new unsigned char[inLength * 2];
|
||||
}
|
||||
void add0()
|
||||
{
|
||||
if (p == 0)
|
||||
claimevent();
|
||||
e *= 2;
|
||||
++p;
|
||||
if (p == 8)
|
||||
addevent();
|
||||
}
|
||||
void add1()
|
||||
{
|
||||
if (p == 0)
|
||||
claimevent();
|
||||
e *= 2;
|
||||
++p;
|
||||
++e;
|
||||
if (p == 8)
|
||||
addevent();
|
||||
}
|
||||
void addbit(int b)
|
||||
{
|
||||
if (b)
|
||||
add1();
|
||||
else
|
||||
add0();
|
||||
}
|
||||
void add3(int b)
|
||||
{
|
||||
addbit(b & 4);
|
||||
addbit(b & 2);
|
||||
addbit(b & 1);
|
||||
}
|
||||
void addvar(int i)
|
||||
{
|
||||
int j = 32768;
|
||||
while (!(i & j))
|
||||
j /= 2;
|
||||
do
|
||||
{
|
||||
if (j == 1)
|
||||
{
|
||||
add0();
|
||||
return;
|
||||
}
|
||||
j/=2;
|
||||
add1();
|
||||
if (i & j)
|
||||
add1();
|
||||
else
|
||||
add0();
|
||||
}
|
||||
while (1);
|
||||
}
|
||||
void adddata(unsigned char d)
|
||||
{
|
||||
outData[dp++] = d;
|
||||
}
|
||||
void addevent()
|
||||
{
|
||||
outData[ep] = e;
|
||||
e = p = 0;
|
||||
}
|
||||
void claimevent()
|
||||
{
|
||||
ep = dp;
|
||||
++dp;
|
||||
}
|
||||
void done()
|
||||
{
|
||||
if (p != 0)
|
||||
{
|
||||
while (p != 8)
|
||||
{
|
||||
e *= 2;
|
||||
++p;
|
||||
}
|
||||
addevent();
|
||||
}
|
||||
FILE *file;
|
||||
if (!(file = fopen(destfilename.c_str(), "wb")))
|
||||
{
|
||||
cout << "Error writing file!\n";
|
||||
exit(1);
|
||||
}
|
||||
fwrite(outData, 1, dp, file);
|
||||
fclose(file);
|
||||
cout << " " << destfilename << ": " << inLength << " -> " << dp << endl;
|
||||
}
|
||||
} s;
|
||||
|
||||
void loadfile(string sourcefilename)
|
||||
{
|
||||
FILE *file;
|
||||
if ((file = fopen(sourcefilename.c_str(), "rb")) == NULL)
|
||||
{
|
||||
cout << "Error opening file:" << sourcefilename << endl;
|
||||
exit(1);
|
||||
}
|
||||
if (!inLength)
|
||||
{
|
||||
fseek(file, 0, SEEK_END);
|
||||
inLength = ftell(file) - offset;
|
||||
}
|
||||
fseek(file, offset, SEEK_SET);
|
||||
inData = new unsigned char[inLength + 1];
|
||||
meta = new metadata[inLength + 1];
|
||||
if (!fread(inData, inLength, 1, file))
|
||||
{
|
||||
cout << "Filesize error" << endl;
|
||||
exit(1);
|
||||
}
|
||||
inData[inLength] = 0;
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
void initvarcost()
|
||||
{
|
||||
int v=1, b=1, r=1;
|
||||
while (r != 65536)
|
||||
{
|
||||
for (int j = 0; j != r; ++j)
|
||||
varcost[v++] = b;
|
||||
b += 2;
|
||||
r *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
void createmetadata()
|
||||
{
|
||||
unsigned i, j;
|
||||
unsigned *last = new unsigned[65536];
|
||||
memset(last, -1, 65536 * sizeof(unsigned));
|
||||
unsigned *prev = new unsigned[inLength + 1];
|
||||
for (i = 0; i != inLength; ++i)
|
||||
{
|
||||
meta[i].cpos[0] = meta[i].clen[0] = 0;
|
||||
prev[i] = last[inData[i] + inData[i + 1] * 256];
|
||||
last[inData[i] + inData[i + 1] * 256] = i;
|
||||
}
|
||||
unsigned r=-1, t=0;
|
||||
for (i = inLength - 1; i != -1; --i)
|
||||
if (inData[i] == r)
|
||||
meta[i].reeks = ++t;
|
||||
else
|
||||
{
|
||||
r = inData[i];
|
||||
meta[i].reeks = t = 1;
|
||||
}
|
||||
for (int bl = 0; bl != 7; ++bl)
|
||||
{
|
||||
for (i = 0; i < inLength; ++i)
|
||||
{
|
||||
unsigned l, p;
|
||||
p = i;
|
||||
if (bl)
|
||||
{
|
||||
meta[i].clen[bl] = meta[i].clen[bl - 1];
|
||||
meta[i].cpos[bl] = meta[i].cpos[bl - 1];
|
||||
p = i - meta[i].cpos[bl];
|
||||
}
|
||||
while((p = prev[p]) != -1)
|
||||
{
|
||||
if (i - p > maxlen[bl])
|
||||
break;
|
||||
l = 0;
|
||||
while (inData[p + l] == inData[i + l] && (i + l) < inLength)
|
||||
{
|
||||
if (meta[i + l].reeks > 1)
|
||||
{
|
||||
if ((j = meta[i + l].reeks) > meta[p + l].reeks)
|
||||
j = meta[p + l].reeks;
|
||||
l+=j;
|
||||
}
|
||||
else
|
||||
++l;
|
||||
}
|
||||
if (l > meta[i].clen[bl])
|
||||
{
|
||||
meta[i].clen[bl] = l;
|
||||
meta[i].cpos[bl] = i - p;
|
||||
}
|
||||
}
|
||||
}
|
||||
cout << ".";
|
||||
}
|
||||
cout << " ";
|
||||
// delete [] prev;
|
||||
// delete [] last;
|
||||
}
|
||||
|
||||
int getlen(pakdata *p, unsigned q)
|
||||
{
|
||||
unsigned i, j, cc, ccc, kc, kmode, kl;
|
||||
p[inLength].cost=0;
|
||||
for (i = inLength - 1; i != -1; --i)
|
||||
{
|
||||
kmode = 0;
|
||||
kl = 0;
|
||||
kc = 9 + p[i + 1].cost;
|
||||
|
||||
j=meta[i].clen[0];
|
||||
while (j > 1)
|
||||
{
|
||||
cc = 9 + varcost[j - 1] + p[i + j].cost;
|
||||
if (cc < kc)
|
||||
{
|
||||
kc = cc;
|
||||
kmode = 1;
|
||||
kl = j;
|
||||
}
|
||||
--j;
|
||||
}
|
||||
|
||||
j = meta[i].clen[q];
|
||||
if (q == 1)
|
||||
ccc = 9;
|
||||
else
|
||||
ccc = 9 + q;
|
||||
while (j > 1)
|
||||
{
|
||||
cc = ccc + varcost[j - 1] + p[i + j].cost;
|
||||
if (cc < kc)
|
||||
{
|
||||
kc = cc;
|
||||
kmode = 2;
|
||||
kl = j;
|
||||
}
|
||||
--j;
|
||||
}
|
||||
|
||||
p[i].cost=kc; p[i].mode=kmode; p[i].mlen=kl;
|
||||
}
|
||||
return p[0].cost;
|
||||
}
|
||||
|
||||
void save(pakdata *p, unsigned q)
|
||||
{
|
||||
s.init();
|
||||
unsigned i, j;
|
||||
|
||||
if (savelength)
|
||||
{
|
||||
s.adddata(inLength & 255);
|
||||
s.adddata(inLength >> 8);
|
||||
}
|
||||
|
||||
s.add3(q-1);
|
||||
s.adddata(inData[0]);
|
||||
|
||||
i=1;
|
||||
while(i<inLength)
|
||||
{
|
||||
switch (p[i].mode)
|
||||
{
|
||||
case 0:
|
||||
s.add0();
|
||||
s.adddata(inData[i]);
|
||||
++i;
|
||||
break;
|
||||
case 1:
|
||||
s.add1();
|
||||
s.addvar(p[i].mlen - 1);
|
||||
j = meta[i].cpos[0] - 1;
|
||||
if (j > 127)
|
||||
cout << "-j>128-";
|
||||
s.adddata(j);
|
||||
i += p[i].mlen;
|
||||
break;
|
||||
case 2:
|
||||
s.add1();
|
||||
s.addvar(p[i].mlen - 1);
|
||||
j = meta[i].cpos[q] - 1;
|
||||
if (j < 128)
|
||||
cout << "-j<128-";
|
||||
j -= 128;
|
||||
s.adddata(128 | j & 127);
|
||||
switch (q)
|
||||
{
|
||||
case 6: s.addbit(j & 4096);
|
||||
case 5: s.addbit(j & 2048);
|
||||
case 4: s.addbit(j & 1024);
|
||||
case 3: s.addbit(j & 512);
|
||||
case 2: s.addbit(j & 256);
|
||||
s.addbit(j & 128);
|
||||
case 1:
|
||||
break;
|
||||
default:
|
||||
cout << "-2-";
|
||||
break;
|
||||
}
|
||||
i += p[i].mlen;
|
||||
break;
|
||||
default:
|
||||
cout << "-?-";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i != 34; ++i)
|
||||
s.add1();
|
||||
s.done();
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (argc == 1)
|
||||
cout << endl;
|
||||
cout << "Pletter v0.5c2 - www.xl2s.tk" << endl;
|
||||
if (argc == 1)
|
||||
{
|
||||
cout << "\nUsage:\npletter [-s[ave_length]] sourcefile [[offset [inLength]] [destinationfile]]\n";
|
||||
exit(0);
|
||||
}
|
||||
|
||||
offset = 0;
|
||||
inLength = 0;
|
||||
|
||||
int i = 1;
|
||||
if (argv[i][0] == '-')
|
||||
{
|
||||
savelength = (argv[i][1] == 's') || (argv[i][1] == 'S');
|
||||
++i;
|
||||
}
|
||||
if (argv[i])
|
||||
sourcefilename = argv[i++];
|
||||
if (argv[i] && isdigit(argv[i][0]))
|
||||
{
|
||||
offset=atoi(argv[i++]);
|
||||
if (argv[i] && isdigit(argv[i][0]))
|
||||
inLength = atoi(argv[i++]);
|
||||
}
|
||||
if (argv[i])
|
||||
destfilename = argv[i++];
|
||||
|
||||
if (!sourcefilename[0])
|
||||
{
|
||||
cout << "No inputfile" << endl;
|
||||
exit(1);
|
||||
}
|
||||
if (!destfilename[0])
|
||||
destfilename = sourcefilename + ".plet5";
|
||||
|
||||
loadfile(sourcefilename);
|
||||
|
||||
initvarcost();
|
||||
createmetadata();
|
||||
|
||||
int minlen = inLength * 1000;
|
||||
int minbl = 0;
|
||||
for(i = 1; i != 7; ++i)
|
||||
{
|
||||
p[i] = new pakdata[inLength + 1];
|
||||
int l = getlen(p[i], i);
|
||||
if (l < minlen && i)
|
||||
{
|
||||
minlen = l;
|
||||
minbl = i;
|
||||
}
|
||||
cout << ".";
|
||||
}
|
||||
save(p[minbl], minbl);
|
||||
|
||||
#ifdef _DEBUG
|
||||
cin.get();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//eof
|
57
pletter.txt
Normal file
57
pletter.txt
Normal file
@ -0,0 +1,57 @@
|
||||
Pletter v0.5c1 - XL2S Entertainment 2008
|
||||
-==============---==================------
|
||||
|
||||
Pletter is a PC based compressor with an MSX based decompressor.
|
||||
|
||||
What has changed
|
||||
-================-
|
||||
|
||||
version 0.5c1
|
||||
- Now also with working pletter.exe.
|
||||
|
||||
version 0.5c
|
||||
- The size of the data can be saved in the file.
|
||||
- Version 0.5c uses a smaller maximum look back buffer size. This means the new unpacker cannot unpack all files packed with version 0.5b; old 0.5 unpackers can unpack 0.5c files though. Unless you let Pletter store the filesize in the file.
|
||||
|
||||
version 0.5a
|
||||
- The size of the lookbackbuffer is stored in the compressed data.
|
||||
|
||||
How to compress
|
||||
-===============-
|
||||
The compressor only works on PC (or any other system that is able to get the source compiled):
|
||||
pletter [-s] <source file> [[offset [length]] [<destination file>]]
|
||||
|
||||
The sourcefile is the file to compress. When the destination file is omitted, it will be constructed the following way: sourcefilename+.plet5
|
||||
|
||||
So: pletter xl2slogo.sc2
|
||||
Will (hopefully ;) result in a file called: xl2slogo.sc2.plet5
|
||||
|
||||
It is possible to compress a part of a file by specifying the start and, optionally, the length:
|
||||
|
||||
pletter fun.sc5 7
|
||||
will skip the bin header and result in a file called fun.sc5.plet5.
|
||||
|
||||
The <20>s option makes Pletter save the length of the original data at the beginning of the file. If you use this option, you have to modify the unpack routine.
|
||||
|
||||
How to decompress
|
||||
-=================-
|
||||
- Include unpack.asm in your sourcefile.
|
||||
- Include the compressed data in your sourcefile.
|
||||
- Call unpack with in HL a pointer to the compressed data, and in DE the destination.
|
||||
|
||||
Example:
|
||||
|
||||
<some code>
|
||||
|
||||
ld hl,data
|
||||
ld de,8000h
|
||||
call unpack
|
||||
|
||||
<more code>
|
||||
|
||||
include unpack.asm ; include the unpacker
|
||||
data
|
||||
incbin data.gfx.plet5 ; the data
|
||||
|
||||
|
||||
If you used to option to include the length of the uncompressed data in the file, you should modify the unpack routine to skip it, or skip it yourself by using ld hl,data+2, or even just incbin "data.gfx.plet5",2.
|
141
unpack.asm
Normal file
141
unpack.asm
Normal file
@ -0,0 +1,141 @@
|
||||
; pletter v0.5c msx unpacker
|
||||
|
||||
; call unpack with hl pointing to some pletter5 data, and de pointing to the destination.
|
||||
; changes all registers
|
||||
|
||||
; define lengthindata when the original size is written in the pletter data
|
||||
|
||||
; define LENGTHINDATA
|
||||
|
||||
module pletter
|
||||
|
||||
macro GETBIT
|
||||
add a,a
|
||||
call z,getbit
|
||||
endmacro
|
||||
|
||||
macro GETBITEXX
|
||||
add a,a
|
||||
call z,getbitexx
|
||||
endmacro
|
||||
|
||||
@unpack
|
||||
|
||||
ifdef LENGTHINDATA
|
||||
inc hl
|
||||
inc hl
|
||||
endif
|
||||
|
||||
ld a,(hl)
|
||||
inc hl
|
||||
exx
|
||||
ld de,0
|
||||
add a,a
|
||||
inc a
|
||||
rl e
|
||||
add a,a
|
||||
rl e
|
||||
add a,a
|
||||
rl e
|
||||
rl e
|
||||
ld hl,modes
|
||||
add hl,de
|
||||
ld e,(hl)
|
||||
ld ixl,e
|
||||
inc hl
|
||||
ld e,(hl)
|
||||
ld ixh,e
|
||||
ld e,1
|
||||
exx
|
||||
ld iy,loop
|
||||
literal
|
||||
ldi
|
||||
loop
|
||||
GETBIT
|
||||
jr nc,literal
|
||||
exx
|
||||
ld h,d
|
||||
ld l,e
|
||||
getlen
|
||||
GETBITEXX
|
||||
jr nc,.lenok
|
||||
.lus
|
||||
GETBITEXX
|
||||
adc hl,hl
|
||||
ret c
|
||||
GETBITEXX
|
||||
jr nc,.lenok
|
||||
GETBITEXX
|
||||
adc hl,hl
|
||||
ret c
|
||||
GETBITEXX
|
||||
jp c,.lus
|
||||
.lenok
|
||||
inc hl
|
||||
exx
|
||||
ld c,(hl)
|
||||
inc hl
|
||||
ld b,0
|
||||
bit 7,c
|
||||
jp z,offsok
|
||||
jp ix
|
||||
|
||||
mode6
|
||||
GETBIT
|
||||
rl b
|
||||
mode5
|
||||
GETBIT
|
||||
rl b
|
||||
mode4
|
||||
GETBIT
|
||||
rl b
|
||||
mode3
|
||||
GETBIT
|
||||
rl b
|
||||
mode2
|
||||
GETBIT
|
||||
rl b
|
||||
GETBIT
|
||||
jr nc,offsok
|
||||
or a
|
||||
inc b
|
||||
res 7,c
|
||||
offsok
|
||||
inc bc
|
||||
push hl
|
||||
exx
|
||||
push hl
|
||||
exx
|
||||
ld l,e
|
||||
ld h,d
|
||||
sbc hl,bc
|
||||
pop bc
|
||||
ldir
|
||||
pop hl
|
||||
jp iy
|
||||
|
||||
getbit
|
||||
ld a,(hl)
|
||||
inc hl
|
||||
rla
|
||||
ret
|
||||
|
||||
getbitexx
|
||||
exx
|
||||
ld a,(hl)
|
||||
inc hl
|
||||
exx
|
||||
rla
|
||||
ret
|
||||
|
||||
modes
|
||||
word offsok
|
||||
word mode2
|
||||
word mode3
|
||||
word mode4
|
||||
word mode5
|
||||
word mode6
|
||||
|
||||
endmodule
|
||||
|
||||
;eof
|
5
unpletter/README.md
Normal file
5
unpletter/README.md
Normal file
@ -0,0 +1,5 @@
|
||||
Unpletter is a C and C++ version of the unpacker for the Pletter compression scheme.
|
||||
|
||||
By Sylvain Glaize, 2024.
|
||||
|
||||
Distributed as the same license as the Pletter packer. See license.txt on the root folder.
|
145
unpletter/unpletter.cpp
Normal file
145
unpletter/unpletter.cpp
Normal file
@ -0,0 +1,145 @@
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
using data_type = unsigned char;
|
||||
|
||||
class Uncompressor
|
||||
{
|
||||
public:
|
||||
explicit Uncompressor(const std::vector<unsigned char>& inData)
|
||||
: compressedData(inData)
|
||||
{
|
||||
std::vector<unsigned char> output;
|
||||
|
||||
int q_value = (getBit() << 2 | getBit() << 1 | getBit()) + 1;
|
||||
|
||||
data_type first_byte = getByte();
|
||||
output.push_back(first_byte);
|
||||
dataPosition = 2;
|
||||
|
||||
while (dataPosition < inData.size())
|
||||
{
|
||||
if (getBit())// Back reference
|
||||
{
|
||||
data_type length = getInterlacedEliasGamma() + 1;
|
||||
|
||||
if (length == 255)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
int offset = getByte();
|
||||
if (offset & 0x80)
|
||||
{
|
||||
offset = offset & 0x7F;
|
||||
switch(q_value)
|
||||
{
|
||||
case 6:
|
||||
offset = offset | (getBit() << 12);
|
||||
case 5:
|
||||
offset = offset | (getBit() << 11);
|
||||
case 4:
|
||||
offset = offset | (getBit() << 10);
|
||||
case 3:
|
||||
offset = offset | (getBit() << 9);
|
||||
case 2:
|
||||
offset = offset | (getBit() << 8);
|
||||
offset = offset | (getBit() << 7);
|
||||
case 1:
|
||||
break;
|
||||
default:
|
||||
std::cout << "Invalid mode: " << q_value << std::endl;
|
||||
break;
|
||||
}
|
||||
offset += 128;
|
||||
}
|
||||
offset += 1;
|
||||
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
output.push_back(output[output.size() - offset]);
|
||||
}
|
||||
}
|
||||
else// Literal data
|
||||
{
|
||||
output.push_back(getByte());
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned char byte: output)
|
||||
{
|
||||
std::cout << byte;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
int dataPosition = 0;
|
||||
int varPosition = 0;
|
||||
int bitForVarPosition = 7;
|
||||
|
||||
const std::vector<unsigned char>& compressedData;
|
||||
|
||||
data_type getByte()
|
||||
{
|
||||
return compressedData[dataPosition++];
|
||||
}
|
||||
|
||||
data_type getBit()
|
||||
{
|
||||
if (bitForVarPosition == 7)
|
||||
{
|
||||
varPosition = dataPosition;
|
||||
dataPosition = varPosition + 1;
|
||||
}
|
||||
|
||||
data_type bit = (compressedData[varPosition] >> bitForVarPosition) & 1;
|
||||
bitForVarPosition--;
|
||||
|
||||
if (bitForVarPosition == -1)
|
||||
{
|
||||
bitForVarPosition = 7;
|
||||
}
|
||||
return bit;
|
||||
}
|
||||
|
||||
data_type getInterlacedEliasGamma()
|
||||
{
|
||||
data_type value = 1;
|
||||
while (getBit())
|
||||
{
|
||||
value = (value << 1) | getBit();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
if (argc < 2)
|
||||
{
|
||||
std::cout << "Usage: " << argv[0] << " <compressed file>" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::ifstream file(argv[1], std::ios::binary | std::ios::ate);
|
||||
if (!file)
|
||||
{
|
||||
std::cout << "Error reading file!" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::streamsize length = file.tellg();
|
||||
file.seekg(0, std::ios::beg);
|
||||
|
||||
std::vector<unsigned char> compressedData(length);
|
||||
if (!file.read(reinterpret_cast<char*>(compressedData.data()), length))
|
||||
{
|
||||
std::cout << "Error reading file!" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
Uncompressor data(compressedData);
|
||||
|
||||
return 0;
|
||||
}
|
146
unpletter/unpletter_c.c
Normal file
146
unpletter/unpletter_c.c
Normal file
@ -0,0 +1,146 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef unsigned char data_type;
|
||||
|
||||
struct Uncompressor {
|
||||
unsigned char* compressedData;
|
||||
int dataPosition;
|
||||
int varPosition;
|
||||
int bitForVarPosition;
|
||||
};
|
||||
|
||||
data_type getByte(struct Uncompressor* uncompressor)
|
||||
{
|
||||
return uncompressor->compressedData[uncompressor->dataPosition++];
|
||||
}
|
||||
|
||||
data_type getBit(struct Uncompressor* uncompressor)
|
||||
{
|
||||
if (uncompressor->bitForVarPosition == 7)
|
||||
{
|
||||
uncompressor->varPosition = uncompressor->dataPosition;
|
||||
uncompressor->dataPosition = uncompressor->varPosition + 1;
|
||||
}
|
||||
|
||||
data_type bit = (uncompressor->compressedData[uncompressor->varPosition] >> uncompressor->bitForVarPosition) & 1;
|
||||
uncompressor->bitForVarPosition--;
|
||||
|
||||
if (uncompressor->bitForVarPosition == -1)
|
||||
{
|
||||
uncompressor->bitForVarPosition = 7;
|
||||
}
|
||||
return bit;
|
||||
}
|
||||
|
||||
data_type getInterlacedEliasGamma(struct Uncompressor* uncompressor)
|
||||
{
|
||||
data_type value = 1;
|
||||
while (getBit(uncompressor))
|
||||
{
|
||||
value = (value << 1) | getBit(uncompressor);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
void uncompress(struct Uncompressor* uncompressor, size_t in_data_length)
|
||||
{
|
||||
unsigned char* output = (unsigned char*) malloc(in_data_length * 3);// * 3 is an arbitrary number...
|
||||
size_t output_position = 0;
|
||||
|
||||
int q_value = (getBit(uncompressor) << 2 | getBit(uncompressor) << 1 | getBit(uncompressor)) + 1;
|
||||
|
||||
data_type first_byte = getByte(uncompressor);
|
||||
output[output_position++] = first_byte;
|
||||
|
||||
while (uncompressor->dataPosition < in_data_length)
|
||||
{
|
||||
if (getBit(uncompressor))
|
||||
{
|
||||
data_type length = getInterlacedEliasGamma(uncompressor) + 1;
|
||||
|
||||
if (length == 255)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
int offset = getByte(uncompressor);
|
||||
if (offset & 0x80)
|
||||
{
|
||||
offset = offset & 0x7F;
|
||||
switch (q_value)
|
||||
{
|
||||
case 6:
|
||||
offset = offset | (getBit(uncompressor) << 12);
|
||||
case 5:
|
||||
offset = offset | (getBit(uncompressor) << 11);
|
||||
case 4:
|
||||
offset = offset | (getBit(uncompressor) << 10);
|
||||
case 3:
|
||||
offset = offset | (getBit(uncompressor) << 9);
|
||||
case 2:
|
||||
offset = offset | (getBit(uncompressor) << 8);
|
||||
offset = offset | (getBit(uncompressor) << 7);
|
||||
case 1:
|
||||
break;
|
||||
default:
|
||||
printf("Invalid mode: %d\n", q_value);
|
||||
break;
|
||||
}
|
||||
offset += 128;
|
||||
}
|
||||
offset += 1;
|
||||
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
output[output_position] = output[output_position - offset];
|
||||
output_position += 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
output[output_position++] = getByte(uncompressor);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < output_position; i++)
|
||||
{
|
||||
printf("%c", output[i]);
|
||||
}
|
||||
free(output);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
if (argc < 2)
|
||||
{
|
||||
printf("Usage: %s <compressed file>\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
FILE* file = fopen(argv[1], "rb");
|
||||
if (!file)
|
||||
{
|
||||
printf("Error reading file!\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
fseek(file, 0, SEEK_END);
|
||||
long length = ftell(file);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
|
||||
unsigned char* compressedData = (unsigned char*) malloc(length);
|
||||
if (!fread(compressedData, length, 1, file))
|
||||
{
|
||||
printf("Error reading file!\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct Uncompressor uncompressor = {compressedData, 0, 0, 7};
|
||||
uncompress(&uncompressor, length);
|
||||
|
||||
free(compressedData);
|
||||
fclose(file);
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user