Skip to content

Instantly share code, notes, and snippets.

@FireyFly
Created July 19, 2015 13:03
Show Gist options
  • Save FireyFly/d9ef476107d8b11cbe7f to your computer and use it in GitHub Desktop.
Save FireyFly/d9ef476107d8b11cbe7f to your computer and use it in GitHub Desktop.
Disgaea DS romfs investigation
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
// 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;
}
#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;
}
// 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