IPB

Добро пожаловать, гость ( Вход | Регистрация )

> East Point Software .EPF file unpacker v2.0
Lumis
Mar 9 2026, 15:47
Сообщение #1


Member
**

Группа: Authorized
Сообщений: 11
Регистрация: 2-March 26
Пользователь №: 18,038
Спасибо сказали: 11 раз(а)



Привет. По просьбе -=CHE@TER=- перегнал в ANSI C и протестировал на EPF от Aladdin 1996 с помощью chatgpt оригинальный код распаковщика ASM + C. В его тестовой среде в докере и у меня на винде отрабатывает чисто.

CODE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#if defined(_MSC_VER) && (_MSC_VER < 1600)
typedef unsigned __int8 uint8_t;
typedef unsigned __int16 uint16_t;
typedef unsigned __int32 uint32_t;
#else
#include <stdint.h>
#endif

#define EPFS_MAGIC 0x53465045UL
#define EPFS_HEADER_SIZE 11
#define EPFS_ITEM_SIZE 22

#define LZW_FIRST_CODE 256
#define LZW_MAX_BITS 14
#define LZW_DICT_CAP 0x4680u /* from original buffers: 0x8D00 bytes / 2 */
#define LZW_STACK_CAP 0x0FA0u /* original hard stop */

typedef struct epfs_item_tag {
char name[14];
uint8_t packed;
uint32_t packed_size;
uint32_t unpacked_size;
} epfs_item;

static uint16_t rd16le(const uint8_t *p)
{
return (uint16_t)((uint16_t)p[0] | ((uint16_t)p[1] << 8));
}

static uint32_t rd32le(const uint8_t *p)
{
return (uint32_t)p[0]
| ((uint32_t)p[1] << 8)
| ((uint32_t)p[2] << 16)
| ((uint32_t)p[3] << 24);
}

static int read_fully(FILE *fp, void *buf, size_t size)
{
return fread(buf, 1, size, fp) == size;
}

static int write_fully(FILE *fp, const void *buf, size_t size)
{
return fwrite(buf, 1, size, fp) == size;
}

static void sanitize_name(char *s)
{
char *p = s;
while (*p) {
unsigned char c = (unsigned char)*p;
if (c < 32 || c == ':' || c == '\\' || c == '/' ||
c == '*' || c == '?' || c == '"' || c == '<' ||
c == '>' || c == '|') {
*p = '_';
}
++p;
}
if (s[0] == '\0') {
strcpy(s, "NONAME.BIN");
}
}

/*
* This is not textbook LZW.
*
* The reset code is (maxvalue - 1), end code is maxvalue.
* The key quirk, matching the original game code:
* reset clears nextcode back to 256, but DOES NOT reset bit width to 9.
*
* If you "fix" that into normal LZW, several real ALADDIN.EPF members fail
* with bogus recursive chains and depth overflow.
*/
static int unlzw_epfs(uint32_t out_size,
const uint8_t *src,
uint32_t src_size,
uint8_t *dst)
{
uint16_t prefix[LZW_DICT_CAP];
uint8_t suffix[LZW_DICT_CAP];
uint8_t stack[LZW_STACK_CAP];

const uint8_t *sp;
const uint8_t *src_end;
uint8_t *dp;
uint8_t *dst_end;

uint32_t bitbuf;
unsigned bitcount;

unsigned curbits;
uint16_t maxvalue;
uint16_t dicreset;
uint16_t incbits;
uint16_t nextcode;

uint16_t prev_code;
uint16_t code;
uint16_t cur;
uint8_t first_char;

sp = src;
src_end = src + src_size;
dp = dst;
dst_end = dst + out_size;

bitbuf = 0;
bitcount = 0;

curbits = 9;
maxvalue = (uint16_t)((1u << curbits) - 1u);
dicreset = (uint16_t)(maxvalue - 1u);
incbits = (uint16_t)(maxvalue - 2u);
nextcode = LZW_FIRST_CODE;

#define GET_CODE(out_code) \
do { \
while (bitcount < curbits) { \
if (sp >= src_end) { \
return 2; \
} \
bitbuf = ((bitbuf << 8) | (uint32_t)(*sp++)) & 0xFFFFFFFFUL; \
bitcount += 8; \
} \
bitcount -= curbits; \
(out_code) = (uint16_t)((bitbuf >> bitcount) & maxvalue); \
} while (0)

#define INC_BITS_IF_NEEDED() \
do { \
if (nextcode > incbits && curbits < LZW_MAX_BITS) { \
++curbits; \
maxvalue = (uint16_t)((1u << curbits) - 1u); \
dicreset = (uint16_t)(maxvalue - 1u); \
incbits = (uint16_t)(maxvalue - 2u); \
} \
} while (0)

if (out_size == 0) {
return 0;
}

GET_CODE(code);
if (code > 0xFFu) {
return 3;
}

*dp++ = (uint8_t)code;
prev_code = code;
first_char = (uint8_t)code;

for (;;) {
unsigned sptr;

if (dp >= dst_end) {
return 0;
}

GET_CODE(code);

if (code == maxvalue) {
return 0;
}

if (code == dicreset) {
nextcode = LZW_FIRST_CODE;

GET_CODE(code);
if (code == maxvalue) {
return 0;
}
if (code > 0xFFu) {
return 3;
}

*dp++ = (uint8_t)code;
prev_code = code;
first_char = (uint8_t)code;
continue;
}

sptr = 0;
cur = code;

if (cur >= nextcode) {
if (sptr >= LZW_STACK_CAP) {
return 1;
}
stack[sptr++] = first_char;
cur = prev_code;
}

while (cur > 0xFFu) {
if (cur >= LZW_DICT_CAP || sptr >= LZW_STACK_CAP) {
return 1;
}
stack[sptr++] = suffix[cur];
cur = prefix[cur];
}

stack[sptr++] = (uint8_t)cur;
first_char = (uint8_t)cur;

while (sptr != 0) {
if (dp >= dst_end) {
return 4;
}
*dp++ = stack[--sptr];
}

if (nextcode < LZW_DICT_CAP) {
prefix[nextcode] = prev_code;
suffix[nextcode] = first_char;
}
++nextcode;
INC_BITS_IF_NEEDED();

prev_code = code;
}

#undef GET_CODE
#undef INC_BITS_IF_NEEDED
}

static int load_item_table(FILE *fp, uint32_t table_offset, uint16_t count, epfs_item **out_items)
{
epfs_item *items;
uint16_t i;

items = (epfs_item *)calloc((size_t)count, sizeof(items[0]));
if (items == NULL) {
return 0;
}

if (fseek(fp, (long)table_offset, SEEK_SET) != 0) {
free(items);
return 0;
}

for (i = 0; i < count; ++i) {
uint8_t raw[EPFS_ITEM_SIZE];
if (!read_fully(fp, raw, sizeof(raw))) {
free(items);
return 0;
}

memcpy(items[i].name, raw, 13);
items[i].name[13] = '\0';
items[i].packed = raw[13];
items[i].packed_size = rd32le(raw + 14);
items[i].unpacked_size = rd32le(raw + 18);
}

*out_items = items;
return 1;
}

static int extract_epf(const char *path)
{
FILE *fp;
uint8_t hdr[EPFS_HEADER_SIZE];
uint32_t magic;
uint32_t table_offset;
uint8_t archive_flags;
uint16_t count;
epfs_item *items;
uint16_t i;
int rc;

fp = fopen(path, "rb");
if (fp == NULL) {
fprintf(stderr, "error: cannot open input file: %s\n", path);
return 2;
}

rc = 3;
items = NULL;

if (!read_fully(fp, hdr, sizeof(hdr))) {
fprintf(stderr, "error: cannot read EPF header\n");
goto done;
}

magic = rd32le(hdr + 0);
table_offset = rd32le(hdr + 4);
archive_flags = hdr[8];
count = rd16le(hdr + 9);

(void)archive_flags;

if (magic != EPFS_MAGIC) {
fprintf(stderr, "error: invalid EPF signature\n");
goto done;
}

if (!load_item_table(fp, table_offset, count, &items)) {
fprintf(stderr, "error: cannot read EPF file table\n");
goto done;
}

if (fseek(fp, EPFS_HEADER_SIZE, SEEK_SET) != 0) {
fprintf(stderr, "error: cannot seek to data area\n");
goto done;
}

for (i = 0; i < count; ++i) {
void *packed_buf;
void *out_buf;
size_t packed_size;
size_t out_size;
char out_name[14];

packed_size = (size_t)items[i].packed_size;
out_size = (size_t)(items[i].packed ? items[i].unpacked_size : items[i].packed_size);

memcpy(out_name, items[i].name, sizeof(out_name));
out_name[13] = '\0';
sanitize_name(out_name);

printf("%s", out_name);

packed_buf = malloc(packed_size ? packed_size : 1u);
if (packed_buf == NULL) {
printf(" - out of memory\n");
rc = 4;
goto done;
}

if (packed_size && !read_fully(fp, packed_buf, packed_size)) {
free(packed_buf);
printf(" - short read\n");
rc = 5;
goto done;
}

if (items[i].packed) {
int urc;

out_buf = malloc(out_size ? out_size : 1u);
if (out_buf == NULL) {
free(packed_buf);
printf(" - out of memory\n");
rc = 4;
goto done;
}

memset(out_buf, 0, out_size);
urc = unlzw_epfs(items[i].unpacked_size,
(const uint8_t *)packed_buf,
items[i].packed_size,
(uint8_t *)out_buf);
free(packed_buf);

if (urc != 0) {
free(out_buf);
printf(" - unpack error %d\n", urc);
rc = 6;
goto done;
}
} else {
out_buf = packed_buf;
}

{
FILE *fo = fopen(out_name, "wb");
if (fo == NULL) {
free(out_buf);
printf(" - cannot create output\n");
rc = 7;
goto done;
}

if (out_size && !write_fully(fo, out_buf, out_size)) {
fclose(fo);
free(out_buf);
printf(" - write error\n");
rc = 8;
goto done;
}

fclose(fo);
}

free(out_buf);
printf("\n");
}

rc = 0;

done:
free(items);
fclose(fp);
return rc;
}

int main(int argc, char **argv)
{
printf("East Point Software .EPF unpacker v2.0\n");
printf("© CTPAX-X Team 2010,2018 © Lumis 2026\nhttp://www.CTPAX-X.org/\n\n");

if (argc != 2) {
printf("usage: %s <file.epf>\n", argv[0]);
return 1;
}

return extract_epf(argv[1]);
}


Спасибо сказали:
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
 
Reply to this topicStart new topic
Ответов

Сообщения в этой теме


Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0 -

 



Упрощённая версия Сейчас: 21st April 2026 - 11:38