forked from zerkman/zpacker
-
Notifications
You must be signed in to change notification settings - Fork 0
/
zpack.c
167 lines (153 loc) · 4.3 KB
/
zpack.c
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
/* -------------------------------------------------------------------
zpack - simple LZ77-based data compression
by Zerkman / Sector One
------------------------------------------------------------------- */
/* Copyright © 2020 François Galea <fgalea at free.fr>
* This program is free software. It comes without any warranty, to
* the extent permitted by applicable law. You can redistribute it
* and/or modify it under the terms of the Do What The Fuck You Want
* To Public License, Version 2, as published by Sam Hocevar. See
* the COPYING file or http://www.wtfpl.net/ for more details. */
#include <stdio.h>
#include <string.h>
static int count_similar(const unsigned char *p1, const unsigned char *p2,
const unsigned char *end) {
int similar = 0;
while (p2 < end && *p1 == *p2) {
++similar;
p1 ++;
p2 ++;
}
return similar;
}
#define ENDMARK
#ifdef ENDMARK
#define flush_individual { \
*w++ = (individual_count) | 0xc0; \
memcpy(w, individual, individual_count); \
w += individual_count; \
individual += individual_count; \
individual_count = 0; }
#else
#define flush_individual { \
*w++ = (individual_count-1) | 0xc0; \
memcpy(w, individual, individual_count); \
w += individual_count; \
individual += individual_count; \
individual_count = 0; }
#endif
long pack(unsigned char *out, const unsigned char *in, long size)
{
unsigned char *w = out;
const unsigned char *r = in;
const unsigned char *end = in + size;
const unsigned char *individual = in;
int individual_count = 0;
while (r < end) {
const unsigned char *p = r-1;
int best_size = 0;
const unsigned char *best_pos = p;
while (p > in && p >= (r-255)) {
int size = count_similar(p, r, end);
if (size > best_size) {
best_size = size;
best_pos = p;
}
p --;
}
if (best_size > 3) {
/* copy */
if (individual_count)
flush_individual;
if (best_size > 0x7f+0x40+4)
best_size = 0x7f+0x40+4;
individual += best_size;
int offset = best_pos - r;
r += best_size;
best_size -= 4;
if (offset >= -256) {
*w++ = best_size;
*w++ = offset;
}
else
printf("problem: offset < -256 !!!\n");
/* printf("size=%d offset=%d\n", best_size, offset); */
} else {
/* individual bytes */
++individual_count;
#ifdef ENDMARK
if (individual_count == 0x3f)
flush_individual;
#else
if (individual_count == 0x40)
flush_individual;
#endif
++r;
}
}
if (individual_count)
flush_individual;
#ifdef ENDMARK
*w++ = 0xc0;
#endif
return w-out;
}
long unpack(unsigned char *out, const unsigned char *in, long size)
{
unsigned char *w = out;
const unsigned char *r = in;
const unsigned char *end = in + size;
while (r < end) {
/* printf("%4x %4x\n", w-out, r-in); */
int size = (unsigned char)(*r++);
if ((size & 0xc0) == 0xc0) {
size &= 0x3f;
#ifdef ENDMARK
if ( size == 0 ) break;
--size;
#endif
while (size-- >= 0)
*w ++ = *r ++;
} else {
int offset;
offset = -256 | (signed char)(*r++);
/* printf("size=%d offset=%d\n", size, offset); */
size += 3;
while (size-- >= 0) {
*w = *(w+offset);
w ++;
}
}
}
return w-out;
}
int main(int argc, char **argv) {
FILE *fd = fopen(argv[1], "r");
if (!fd) {
perror(argv[1]);
return 1;
}
fseek(fd, 0, SEEK_END);
long in_size = ftell(fd);
fseek(fd, 0, SEEK_SET);
unsigned char in_file[in_size];
unsigned char out_file[in_size*2];
fread(in_file, 1, in_size, fd);
fclose(fd);
long packed_size = pack(out_file, in_file, in_size);
printf("size=%ld packed=%ld\n", in_size, packed_size);
unsigned char buffer[in_size*2];
long unpacked_size = unpack(buffer, out_file, packed_size);
if (memcmp(in_file, buffer, in_size)) {
printf("Problem %ld %ld\n", in_size, unpacked_size);
fd = fopen("out.upk", "w");
fwrite(buffer, 1, unpacked_size, fd);
fclose(fd);
}
else
printf("Depack ok\n");
fd = fopen("out.pck", "w");
fwrite(out_file, 1, packed_size, fd);
fclose(fd);
return 0;
}