Created
July 19, 2015 13:03
-
-
Save FireyFly/d9ef476107d8b11cbe7f to your computer and use it in GitHub Desktop.
Disgaea DS romfs investigation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File tree Omitted files Type | |
. | |
├── ThumbBg.dat DSARCIDX | |
│ └···Thubg*.imy ( 19 files) IMY | |
├── bg.dat DSARCIDX | |
│ └···bg*.mpb (117 files) MAP/IMY | |
├── bgm.dat DSARCFL | |
│ └···bgm_*.msnd ( 33 files) DSEQ | |
├── bgm.dat.tbl DATTBL | |
├── bu.dat DSARCIDX | |
│ └···bu*.mpb (101 files) MAP/IMY | |
├── ch_*.amd (234 files) AMD (multiple magic $FACE, small) | |
├── ch_*.amt (234 files) AMT (magic $FACE, bigger) | |
├── chclut.bin | |
├── chtex.bin 12BYTES-ARMS | |
├── demoDbg.dat DSARCIDX | |
│ └···file* (280 files) ASCII text | |
├── dspack.dat DSARCIDX | |
│ ├···back.imy IMY | |
│ ├···base.dso DSO (DSARCIDX) | |
│ │ ├···base.dsm DSM | |
│ │ └···baseoe_*.imy ( 5 files) IMY | |
│ ├···cursor.dso DSO (DSARCIDX) | |
│ │ ├···cursor.dsm DSM | |
│ │ └···cursoroe_*.imy ( 2 files) IMY | |
│ ├···enn.dso DSO (DSARCIDX) | |
│ │ ├···enn.dsm DSM | |
│ │ └···ennoe_*.imy ( 2 files) IMY | |
│ ├···enn.imy IMY | |
│ ├···establish*.imy ( 23 files) IMY | |
│ ├···helpbg*.imy ( 7 files) IMY | |
│ ├···loading*.imy ( 6 files) IMY | |
│ ├···mgate.dso DSO (DSARCIDX) | |
│ │ ├···mgate.dsm DSM | |
│ │ └···mgateoe_*.imy ( 4 files) IMY | |
│ ├···pliny01.imy IMY | |
│ ├···sys7.imy IMY | |
│ ├···taiho.dso DSO (DSARCIDX) | |
│ │ ├···taiho.dsm DSM | |
│ │ └···taihooe_*.imy ( 6 files) IMY | |
│ ├···taiho2.dso DSO (DSARCIDX) | |
│ │ ├···taiho2.dsm DSM | |
│ │ └···taiho2oe_*.imy ( 4 files) IMY | |
│ ├···titdemo3.imy IMY | |
│ ├···titdemo4.imy IMY | |
│ ├···titdemo5.imy IMY | |
│ ├···waku3.imy IMY | |
│ └···wbg*.imy ( 17 files) IMY | |
├── efctobj.dat DSARCIDX | |
│ └···effect*.dsm (133 files) DSM | |
├── efcttex.dat DSARCIDX | |
│ └···effect*_*.imy (270 files) IMY | |
├── mapobjs.dat DSARCIDX | |
│ └···mp*o*.dsm (234 files) DSM | |
├── maptex.dat DSARCIDX | |
│ └···mp*{t,prd}*.imy (785 files) IMY | |
├── mpds.dat DSARCIDX | |
│ └···mp*.mpds (198 files) MPDS | |
├── mpds.lst ASCII text | |
├── msgvo.dat DSARCFL | |
│ └···*.strm (173 files) STRM | |
├── msgvo.dat.tbl DATTBL | |
├── nafnt.fnt | |
├── objtex.dat DSARCIDX | |
│ └···mp*o*_*.imy (646 files) IMY | |
├── rmaps.dat DSARCIDX | |
│ └···20*.rmd ( 72 files) RMD | |
├── script.dat | |
├── se.dat DSARCFL | |
│ ├···*_*.se (128 files) SWAV | |
│ └···yrah*.swav ( 4 files) (empty) | |
├── se.dat.tbl DATTBL | |
├── song.dat DSARCFL | |
│ └···yrah*.swav ( 12 files) DSEQ | |
├── song.dat.tbl DATTBL | |
├── sprts.bin | |
├── sys9.imy IMY | |
├── table.dat DSARCIDX | |
│ ├···char.dat table | |
│ ├···charhelp.dat table | |
│ ├···dungeon.dat table | |
│ ├···ge.dat table | |
│ ├···geocube.dat table | |
│ ├···habit.dat table | |
│ ├···hospital.dat table | |
│ ├···magic.dat table | |
│ ├···mitem.dat table | |
│ ├···musicshop.dat table | |
│ ├···name.dat table | |
│ ├···thief.dat table | |
│ ├···wish.dat table | |
│ └···zukan.dat table | |
├── talk.dat | |
├── uwbg*.mpb ( 12 files) MAP/IMY | |
├── voice.dat DSARCFL | |
│ ├···*_*.se ( 3 files) SWAV | |
│ └···*_*.swav (185 files) SWAV | |
├── voice.dat.tbl DATTBL | |
├── waku.imy IMY | |
├── waku2.imy IMY | |
├── waku3.imy IMY | |
├── wbg.imy IMY | |
├── wbg2.imy IMY | |
└── wbg3.imy IMY |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// TODO: investigate the metadata between the file blobs | |
#include <ctype.h> | |
#include <stdint.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
typedef uint8_t u8; | |
typedef uint16_t u16; | |
typedef uint32_t u32; | |
#define FMT_END "\x1B[m" | |
/** SGR string to format the byte `v`. */ | |
const char *format_of(u32 v) { | |
return v == 0x00? "\x1B[38;5;238m" | |
: v == 0xFF? "\x1B[38;5;167m" | |
: v < 0x20? "\x1B[38;5;150m" | |
: v >= 0x7F? "\x1B[38;5;141m" | |
: FMT_END; | |
} | |
/** Hexdump `n` bytes at `p`. */ | |
void hexdump(void *p_, int n) { | |
u8 *p = p_; | |
int cols = 16, i; | |
for (i = 0; i < n; i += cols) { | |
// Offset | |
printf(" %04x ", i); | |
// Read line | |
int line[cols]; | |
for (int j = 0; j < cols; j++) { | |
line[j] = i + j < n? *p++ : -1; | |
} | |
// Print hex area | |
for (int j = 0; j < cols; j++) { | |
int v = line[j]; | |
if (v >= 0) printf(" %s%02x" FMT_END, format_of(v), v); | |
else printf(" "); | |
if (j % 8 == 7) printf(" "); | |
} | |
printf(" "); | |
// Print character area | |
for (int j = 0; j < cols; j++) { | |
int v = line[j]; | |
if (v >= 0) printf("%s%c" FMT_END, format_of(v), isprint(v)? v : '.'); | |
else printf(" "); | |
if (j % 8 == 7) putchar(" \n"[j == cols - 1]); | |
} | |
} | |
if (i % cols != 0) putchar('\n'); | |
} | |
struct header { | |
char magic[8]; | |
u32 count; | |
u32 zero1; | |
} __attribute__((packed)); | |
struct file_entry { | |
char filename[40]; | |
u32 size; | |
u32 offset; | |
} __attribute__((packed)); | |
struct archive_header { | |
int count; | |
u16 *ids; | |
struct file_entry *entries; | |
}; | |
struct archive_header *read_archive_header(FILE *f) { | |
struct archive_header *archeader = malloc(sizeof(struct archive_header)); | |
struct header hd; | |
fread(&hd, sizeof(struct header), 1, f); | |
u16 *file_ids = malloc(sizeof(u16) * hd.count); | |
struct file_entry *entries = malloc(sizeof(struct file_entry) * hd.count); | |
fread(file_ids, sizeof(u16), hd.count, f); | |
// Skip padding to multiple of 4 bytes (TODO: might actually be 8; verify) | |
// Note: first file ID starts at a $10 boundary. | |
int skip = 4 - (hd.count * 2 % 4); | |
if (skip == 4) skip = 0; | |
fseek(f, skip, SEEK_CUR); | |
fread(entries, sizeof(struct file_entry), hd.count, f); | |
archeader->count = hd.count; | |
archeader->ids = file_ids; | |
archeader->entries = entries; | |
return archeader; | |
} | |
void free_archive_header(struct archive_header *hd) { | |
free(hd->ids); | |
free(hd->entries); | |
free(hd); | |
} | |
void debug_dump_metadata(struct archive_header *archeader, FILE *f) { | |
long last = ftell(f); | |
char *buf = NULL; | |
for (int i = 0; i < archeader->count; i++) { | |
struct file_entry *ent = &archeader->entries[i]; | |
long delta = ent->offset - last; | |
printf("%06lx %06x %06lx\n", last, ent->offset, delta); | |
buf = realloc(buf, delta); | |
fseek(f, last, SEEK_SET); | |
fread(buf, 1, delta, f); | |
printf("[%3d] %-30s $%06x +$%06x\n", archeader->ids[i], | |
ent->filename, | |
ent->offset, ent->size); | |
hexdump(buf, delta); | |
printf("\n"); | |
last = ent->offset + ent->size; | |
} | |
} | |
void list_files(struct archive_header *archeader) { | |
printf("\x1B[1m%5s %-20s %-8s %-8s\x1B[m\n", "#", "Filename", "Offset", "Size"); | |
for (int i = 0; i < archeader->count; i++) { | |
struct file_entry *ent = &archeader->entries[i]; | |
printf("%5d %-20s $%07x $%07x\n", archeader->ids[i], | |
ent->filename, | |
ent->offset, ent->size); | |
} | |
} | |
void file_copy(FILE *to_f, FILE *from_f, size_t count) { | |
#define SIZE 4096 | |
char buf[SIZE]; | |
size_t remaining = count; | |
while (remaining > 0) { | |
size_t n = fread(buf, 1, remaining < SIZE? remaining : SIZE, from_f); | |
if (n == 0) break; | |
fwrite(buf, 1, n, to_f); | |
remaining -= n; | |
} | |
#undef SIZE | |
} | |
void extract_files(const char *root, struct archive_header *archeader, FILE *f) { | |
char buf[1024]; | |
for (int i = 0; i < archeader->count; i++) { | |
struct file_entry *ent = &archeader->entries[i]; | |
sprintf(buf, "%s/%s", root, ent->filename); | |
printf("write %s\n", buf); | |
// File: extract to corresponding file | |
FILE *f2 = fopen(buf, "wb"); | |
if (f2 == NULL) { | |
fprintf(stderr, "fatal: couldn't open \"%s\" for writing.\n", buf); | |
exit(3); | |
} | |
fseek(f, ent->offset, SEEK_SET); | |
file_copy(f2, f, ent->size); | |
fclose(f2); | |
} | |
} | |
//-- Entry point ---------------------------------------------------- | |
enum operation { | |
OP_LIST, | |
OP_EXTRACT, | |
OP_DEBUG, | |
}; | |
int read_operation(const char *s) { | |
if (strcmp(s, "d") == 0 || strcmp(s, "debug") == 0) return OP_DEBUG; | |
if (strcmp(s, "l") == 0 || strcmp(s, "list") == 0) return OP_LIST; | |
if (strcmp(s, "x") == 0 || strcmp(s, "extract") == 0) return OP_EXTRACT; | |
return -1; | |
} | |
int main(int argc, char *argv[]) { | |
if (argc != 3) { | |
fprintf(stderr, "Usage: %s <op> <file>\n", argv[0]); | |
exit(1); | |
} | |
int operation = read_operation(argv[1]); | |
FILE *f = fopen(argv[2], "r"); | |
struct archive_header *archeader = read_archive_header(f); | |
switch (operation) { | |
case OP_DEBUG: | |
debug_dump_metadata(archeader, f); | |
break; | |
case OP_LIST: | |
list_files(archeader); | |
break; | |
case OP_EXTRACT: | |
extract_files(".", archeader, f); | |
break; | |
default: | |
fprintf(stderr, "Unimplemented operation: '%s'\n", argv[1]); | |
return 2; | |
} | |
free_archive_header(archeader); | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <assert.h> | |
#include <ctype.h> | |
#include <stdint.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
typedef uint8_t u8; | |
typedef uint16_t u16; | |
typedef uint32_t u32; | |
#define FMT_END "\x1B[m" | |
/** SGR string to format the byte `v`. */ | |
const char *format_of(u32 v) { | |
return v == 0x00? "\x1B[38;5;238m" | |
: v == 0xFF? "\x1B[38;5;167m" | |
: v < 0x20? "\x1B[38;5;150m" | |
: v >= 0x7F? "\x1B[38;5;141m" | |
: FMT_END; | |
} | |
/** Hexdump `n` bytes at `p`. */ | |
void hexdump(void *p_, int n) { | |
u8 *p = p_; | |
int cols = 16, i; | |
for (i = 0; i < n; i += cols) { | |
// Offset | |
printf(" %04x ", i); | |
// Read line | |
int line[cols]; | |
for (int j = 0; j < cols; j++) { | |
line[j] = i + j < n? *p++ : -1; | |
} | |
// Print hex area | |
for (int j = 0; j < cols; j++) { | |
int v = line[j]; | |
if (v >= 0) printf(" %s%02x" FMT_END, format_of(v), v); | |
else printf(" "); | |
if (j % 8 == 7) printf(" "); | |
} | |
printf(" "); | |
// Print character area | |
for (int j = 0; j < cols; j++) { | |
int v = line[j]; | |
if (v >= 0) printf("%s%c" FMT_END, format_of(v), isprint(v)? v : '.'); | |
else printf(" "); | |
if (j % 8 == 7) putchar(" \n"[j == cols - 1]); | |
} | |
} | |
if (i % cols != 0) putchar('\n'); | |
} | |
struct header { | |
char magic[8]; | |
u32 count; | |
u32 unk1; | |
} __attribute__((packed)); | |
struct file_entry { | |
char filename[40]; | |
u32 size; | |
u32 offset; | |
} __attribute__((packed)); | |
struct archive_header { | |
int count; | |
struct file_entry *entries; | |
}; | |
struct archive_header *read_archive_header(FILE *f) { | |
struct archive_header *archeader = malloc(sizeof(struct archive_header)); | |
struct header hd; | |
fread(&hd, sizeof(struct header), 1, f); | |
/* | |
u16 *file_ids = malloc(sizeof(u16) * hd.count); | |
fread(file_ids, sizeof(u16), hd.count, f); | |
// Skip padding to multiple of 4 bytes (TODO: might actually be 8; verify) | |
// Note: first file ID starts at a $10 boundary. | |
int skip = 4 - (hd.count * 2 % 4); | |
if (skip == 4) skip = 0; | |
fseek(f, skip, SEEK_CUR); | |
*/ | |
struct file_entry *entries = malloc(sizeof(struct file_entry) * hd.count); | |
if (strncmp(hd.magic, "DSARC FL", 8) != 0) { | |
fprintf(stderr, "Bad magic number!\n"); | |
exit(2); | |
} | |
assert(hd.unk1 == 1); | |
fread(entries, sizeof(struct file_entry), hd.count, f); | |
archeader->count = hd.count; | |
archeader->entries = entries; | |
return archeader; | |
} | |
void free_archive_header(struct archive_header *hd) { | |
free(hd->entries); | |
free(hd); | |
} | |
void debug_dump_metadata(struct archive_header *archeader, FILE *f) { | |
long last = ftell(f); | |
char *buf = NULL; | |
for (int i = 0; i < archeader->count; i++) { | |
struct file_entry *ent = &archeader->entries[i]; | |
long delta = ent->offset - last; | |
printf("%06lx %06x %06lx\n", last, ent->offset, delta); | |
buf = realloc(buf, delta); | |
fseek(f, last, SEEK_SET); | |
fread(buf, 1, delta, f); | |
printf("%-30s $%06x +$%06x\n", ent->filename, | |
ent->offset, ent->size); | |
hexdump(buf, delta); | |
printf("\n"); | |
last = ent->offset + ent->size; | |
} | |
} | |
void list_files(struct archive_header *archeader) { | |
printf("\x1B[1m %-20s %-8s %-8s\x1B[m\n", "Filename", "Offset", "Size"); | |
for (int i = 0; i < archeader->count; i++) { | |
struct file_entry *ent = &archeader->entries[i]; | |
printf(" %-20s $%07x $%07x\n", ent->filename, ent->offset, ent->size); | |
} | |
} | |
void file_copy(FILE *to_f, FILE *from_f, size_t count) { | |
#define SIZE 4096 | |
char buf[SIZE]; | |
size_t remaining = count; | |
while (remaining > 0) { | |
size_t n = fread(buf, 1, remaining < SIZE? remaining : SIZE, from_f); | |
if (n == 0) break; | |
fwrite(buf, 1, n, to_f); | |
remaining -= n; | |
} | |
#undef SIZE | |
} | |
void extract_files(const char *root, struct archive_header *archeader, FILE *f) { | |
char buf[1024]; | |
for (int i = 0; i < archeader->count; i++) { | |
struct file_entry *ent = &archeader->entries[i]; | |
sprintf(buf, "%s/%s", root, ent->filename); | |
printf("write %s\n", buf); | |
// File: extract to corresponding file | |
FILE *f2 = fopen(buf, "wb"); | |
if (f2 == NULL) { | |
fprintf(stderr, "fatal: couldn't open \"%s\" for writing.\n", buf); | |
exit(3); | |
} | |
fseek(f, ent->offset, SEEK_SET); | |
file_copy(f2, f, ent->size); | |
fclose(f2); | |
} | |
} | |
//-- Entry point ---------------------------------------------------- | |
enum operation { | |
OP_LIST, | |
OP_EXTRACT, | |
OP_DEBUG, | |
}; | |
int read_operation(const char *s) { | |
if (strcmp(s, "d") == 0 || strcmp(s, "debug") == 0) return OP_DEBUG; | |
if (strcmp(s, "l") == 0 || strcmp(s, "list") == 0) return OP_LIST; | |
if (strcmp(s, "x") == 0 || strcmp(s, "extract") == 0) return OP_EXTRACT; | |
return -1; | |
} | |
int main(int argc, char *argv[]) { | |
if (argc != 3) { | |
fprintf(stderr, "Usage: %s <op> <file>\n", argv[0]); | |
exit(1); | |
} | |
int operation = read_operation(argv[1]); | |
FILE *f = fopen(argv[2], "r"); | |
struct archive_header *archeader = read_archive_header(f); | |
switch (operation) { | |
case OP_DEBUG: | |
debug_dump_metadata(archeader, f); | |
break; | |
case OP_LIST: | |
list_files(archeader); | |
break; | |
case OP_EXTRACT: | |
extract_files(".", archeader, f); | |
break; | |
default: | |
fprintf(stderr, "Unimplemented operation: '%s'\n", argv[1]); | |
return 2; | |
} | |
free_archive_header(archeader); | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// For the .dat.tbl files accompanying DSARCFL .dat's. | |
#include <stdint.h> | |
#include <stdio.h> | |
typedef uint8_t u8; | |
typedef uint16_t u16; | |
typedef uint32_t u32; | |
struct header { | |
u32 count; | |
char filename[28]; | |
} __attribute__((packed)); | |
int main(int argc, char *argv[]) { | |
if (argc != 2) { | |
fprintf(stderr, "Usage: %s <filename>\n", argv[0]); | |
return 1; | |
} | |
FILE *f = fopen(argv[1], "r"); | |
if (f == NULL) { | |
fprintf(stderr, "Couldn't open %s for reading.\n", argv[1]); | |
return 1; | |
} | |
struct header hd; | |
fread(&hd, sizeof(struct header), 1, f); | |
printf("%s: %d entries\n", hd.filename, hd.count); | |
printf("\x1B[1m%3s %8s %8s %8s %8s %8s\x1B[m\n", "#", "ID", "?", "?", "?", "?"); | |
for (int i = 0; i < hd.count; i++) { | |
#define NFIELDS 5 | |
u32 fields[NFIELDS]; | |
for (int j = 0; j < NFIELDS; j++) { | |
fseek(f, 0x20 + (j*hd.count + i)*4, SEEK_SET); | |
fread(&fields[j], sizeof(u32), 1, f); | |
} | |
printf("%3d:", i); | |
for (int j = 0; j < NFIELDS; j++) { | |
printf(" %08x", fields[j]); | |
} | |
printf("\n"); | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment