DOS SC2 string convertor.
git-svn-id: svn://svn.code.sf.net/p/sc2/code/trunk@2738 8092fc87-c524-0410-9efc-e669fe64eaf9
This commit is contained in:
@@ -0,0 +1,9 @@
|
|||||||
|
unstr: unstr.c
|
||||||
|
gcc -W -Wall -g -O0 unstr.c -o unstr
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm unstr unstr.tgz
|
||||||
|
|
||||||
|
tgz: unstr.c Makefile
|
||||||
|
tar -cvzf unstr.tgz unstr.c Makefile
|
||||||
|
|
||||||
Executable
BIN
Binary file not shown.
@@ -0,0 +1,311 @@
|
|||||||
|
/*
|
||||||
|
* DOS SC2 string extractor.
|
||||||
|
*
|
||||||
|
* By Serge van den Boom (svdb@stack.nl), 20021027
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
// The DOS strings are contained within a dynamically loaded piece of 16
|
||||||
|
// bits x86 code. These code files also contain a table with starting
|
||||||
|
// offsets of the various strings.
|
||||||
|
// This program is very simple; it takes the filename of the resource,
|
||||||
|
// and will then search backwards from the end of the file to the start
|
||||||
|
// off the string table. It will then print the strings in a human-readable
|
||||||
|
// format.
|
||||||
|
//
|
||||||
|
// To use this program, you need to take the following steps:
|
||||||
|
// - use unpkg to extract the various conversation packages, given
|
||||||
|
// starcon.pkg, con1.pkg, and con2.pkg. (starcon.pkg contains the index;
|
||||||
|
// the actual data comes from con1.pkg and con2.pkg)
|
||||||
|
// - use unpkg on the extracted con packages. The con packages all have
|
||||||
|
// resource type 6 (the last two numbers of the resource id presented as
|
||||||
|
// a string are "06"). This produces some files; the files with resource
|
||||||
|
// id 00200007 and 00400007 are the ones we're interested in.
|
||||||
|
// - on these files, run "lz" from the "dos-ani" directory, to decompress
|
||||||
|
// them.
|
||||||
|
// - run this program, unstr, on the decompressed files.
|
||||||
|
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
//#include <sys/param.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
|
||||||
|
typedef uint8_t uint8;
|
||||||
|
typedef uint16_t uint16;
|
||||||
|
typedef int32_t int32;
|
||||||
|
|
||||||
|
#define MAKE_WORD(x1, x2) (((x2) << 8) | (x1))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
struct options {
|
||||||
|
char *infile;
|
||||||
|
off_t startOffset;
|
||||||
|
off_t endOffset;
|
||||||
|
char verbose;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
bool testStringTable(const uint8 *data, const uint8 *start,
|
||||||
|
const uint8 *end);
|
||||||
|
void findStringTable(const uint8 *data, size_t len, off_t *tableStart,
|
||||||
|
off_t *tableEnd);
|
||||||
|
void parse_arguments(int argc, char *argv[], struct options *opts);
|
||||||
|
void dumpStrings(const uint8 *data, off_t startOff, off_t endOff);
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char *argv[]) {
|
||||||
|
int in;
|
||||||
|
struct stat sb;
|
||||||
|
uint8 *buf;
|
||||||
|
struct options opts;
|
||||||
|
|
||||||
|
parse_arguments(argc, argv, &opts);
|
||||||
|
|
||||||
|
in = open(opts.infile, O_RDONLY);
|
||||||
|
if (in == -1) {
|
||||||
|
fprintf(stderr, "Error: Could not open file %s: %s\n", opts.infile,
|
||||||
|
strerror(errno));
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fstat(in, &sb) == -1) {
|
||||||
|
perror("stat() failed");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, in, 0);
|
||||||
|
if (buf == MAP_FAILED) {
|
||||||
|
perror("mmap() failed");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
close(in);
|
||||||
|
|
||||||
|
if (opts.startOffset == (off_t) -1) {
|
||||||
|
findStringTable(buf, sb.st_size, &opts.startOffset,
|
||||||
|
&opts.endOffset);
|
||||||
|
if (opts.verbose) {
|
||||||
|
fprintf(stderr, "String table found at offset %u "
|
||||||
|
"(0x%08x) to %u (0x%08x).\n",
|
||||||
|
(unsigned int) opts.startOffset,
|
||||||
|
(unsigned int) opts.startOffset,
|
||||||
|
(unsigned int) opts.endOffset,
|
||||||
|
(unsigned int) opts.endOffset);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (opts.endOffset == (off_t) -1)
|
||||||
|
opts.endOffset = sb.st_size;
|
||||||
|
|
||||||
|
if (opts.startOffset > opts.endOffset ||
|
||||||
|
opts.endOffset > sb.st_size) {
|
||||||
|
fprintf(stderr, "Invalid offset.\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dumpStrings(buf, opts.startOffset, opts.endOffset);
|
||||||
|
|
||||||
|
munmap(buf, sb.st_size);
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
usage() {
|
||||||
|
fprintf(stderr, "unstr [-s <startOffset> [-e <endOffset>]] <infile>\n"
|
||||||
|
"\t-s start of the string table. If not specified, unstr tries\n"
|
||||||
|
"\t to locate it itself.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
parse_arguments(int argc, char *argv[], struct options *opts) {
|
||||||
|
char ch;
|
||||||
|
|
||||||
|
memset(opts, '\0', sizeof (struct options));
|
||||||
|
opts->startOffset = (off_t) -1;
|
||||||
|
opts->endOffset = (off_t) -1;
|
||||||
|
while (1) {
|
||||||
|
ch = getopt(argc, argv, "s:v");
|
||||||
|
if (ch == -1)
|
||||||
|
break;
|
||||||
|
switch(ch) {
|
||||||
|
case 'v':
|
||||||
|
opts->verbose = 1;
|
||||||
|
break;
|
||||||
|
case 'e': {
|
||||||
|
char *strEnd;
|
||||||
|
long value;
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
value = strtoul(optarg, &strEnd, 0);
|
||||||
|
if (errno != 0 || *optarg == '\0' || *strEnd != '\0') {
|
||||||
|
fprintf(stderr, "Invalid argument to '-e'.\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
opts->endOffset = (off_t) value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 's': {
|
||||||
|
char *strEnd;
|
||||||
|
long value;
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
value = strtoul(optarg, &strEnd, 0);
|
||||||
|
if (errno != 0 || *optarg == '\0' || *strEnd != '\0') {
|
||||||
|
fprintf(stderr, "Invalid argument to '-s'.\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
opts->startOffset = (off_t) value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case '?':
|
||||||
|
case 'h':
|
||||||
|
default:
|
||||||
|
usage();
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts->startOffset == (off_t) -1 && opts->endOffset != (off_t) -1) {
|
||||||
|
fprintf(stderr, "'-e' is only valid in combination with '-s'.\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
argc -= optind;
|
||||||
|
argv += optind;
|
||||||
|
if (argc != 1) {
|
||||||
|
usage();
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
opts->infile = argv[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pre: start and end are aligned on 2 bytes.
|
||||||
|
bool
|
||||||
|
testStringTable(const uint8 *data, const uint8 *start, const uint8 *end) {
|
||||||
|
const uint8 *ptr;
|
||||||
|
off_t tableStartOff = start - data;
|
||||||
|
int32 lastWord;
|
||||||
|
|
||||||
|
lastWord = -1;
|
||||||
|
for (ptr = start; ptr < end; ptr += 2) {
|
||||||
|
uint16 word = MAKE_WORD(ptr[0], ptr[1]);
|
||||||
|
|
||||||
|
// Check the range.
|
||||||
|
if (word >= tableStartOff)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Offsets must be increasing.
|
||||||
|
if (word <= lastWord)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// XXX: I could whether the strings end where the next one starts,
|
||||||
|
// but this scanning is good enough.
|
||||||
|
|
||||||
|
lastWord = word;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
findStringTable(const uint8 *data, size_t len, off_t *tableStart,
|
||||||
|
off_t *tableEnd) {
|
||||||
|
off_t i = (off_t) len;
|
||||||
|
|
||||||
|
// Table is aligned on 2 bytes.
|
||||||
|
if (i % 2 == 1)
|
||||||
|
i--;
|
||||||
|
|
||||||
|
while (i >= 2) {
|
||||||
|
uint16 word;
|
||||||
|
const uint8 *ptr;
|
||||||
|
i -= 2;
|
||||||
|
|
||||||
|
// Check whether the word points to someplace before &data[i].
|
||||||
|
word = MAKE_WORD(data[i], data[i + 1]);
|
||||||
|
if (word >= i)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// There should be one string starting at &data[word], and ending
|
||||||
|
// just before the start of the string table.
|
||||||
|
ptr = memchr(&data[word], '\0', i - word);
|
||||||
|
if (ptr == NULL)
|
||||||
|
continue;
|
||||||
|
// If the pointer to the end of the string is an even position,
|
||||||
|
// the next byte must be '\0' too.
|
||||||
|
if (((uintptr_t) ptr % 2) == 0) {
|
||||||
|
ptr++;
|
||||||
|
if (*ptr != '\0')
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ptr++;
|
||||||
|
// Ptr now points past the end of the string. The start of the
|
||||||
|
// string table could be here.
|
||||||
|
|
||||||
|
if (testStringTable(data, ptr, &data[i + 2])) {
|
||||||
|
*tableStart = ptr - data;
|
||||||
|
*tableEnd = i + 2;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "Could not find the start of the string table.\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pre: startOff and endOff fall within the range of data.
|
||||||
|
void
|
||||||
|
dumpStrings(const uint8 *data, off_t startOff, off_t endOff) {
|
||||||
|
const uint8 *start = data + startOff;
|
||||||
|
const uint8 *end = data + endOff;
|
||||||
|
const uint8 *maxStr = start;
|
||||||
|
// The strings come directly before the string table.
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; start < end; start += 2, i++) {
|
||||||
|
uint16 strOff = MAKE_WORD(start[0], start[1]);
|
||||||
|
size_t maxLen;
|
||||||
|
const char *strEnd;
|
||||||
|
|
||||||
|
if (&data[strOff] >= maxStr) {
|
||||||
|
fprintf(stderr, "Invalid string table entry on offset %u.\n",
|
||||||
|
(uint16) (start - data));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
maxLen = (size_t) (maxStr - &data[strOff]);
|
||||||
|
strEnd = memchr(&data[strOff], '\0', maxLen);
|
||||||
|
if (strEnd == NULL) {
|
||||||
|
fprintf(stderr, "Invalid string table entry on offset %u.\n",
|
||||||
|
(uint16) (start - data));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("#%d\n%s\n", i, (const char *) &data[strOff]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Executable
+44
@@ -0,0 +1,44 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
UNSTR="./unstr"
|
||||||
|
INPREFIX="./"
|
||||||
|
OUTPREFIX="./dialog/"
|
||||||
|
INFORMAT='${INPREFIX}${RACE1}.con/$ID.out'
|
||||||
|
OUTFORMAT='${OUTPREFIX}${RACE2}.txt'
|
||||||
|
|
||||||
|
|
||||||
|
while read RACE1 ID RACE2; do
|
||||||
|
eval INFILE="$INFORMAT"
|
||||||
|
eval OUTFILE="$OUTFORMAT"
|
||||||
|
echo "Processing... $INFILE ==> $OUTFILE"
|
||||||
|
$UNSTR -v "$INFILE" > "$OUTFILE"
|
||||||
|
done << EOF
|
||||||
|
arilou 00200007 arilou
|
||||||
|
blackur 00200007 blackur
|
||||||
|
chmmr 00200007 chmmr
|
||||||
|
comandr 00200007 comandr
|
||||||
|
comandr 00400107 starbas
|
||||||
|
druuge 00200007 druuge
|
||||||
|
ilwrath 00200007 ilwrath
|
||||||
|
melnorm 00200007 melnorm
|
||||||
|
mycon 00200007 mycon
|
||||||
|
orz 00200007 orz
|
||||||
|
pkunk 00200007 pkunk
|
||||||
|
shofixt 00200007 shofixt
|
||||||
|
slyhome 00200007 slyhome
|
||||||
|
slyland 00200007 slyland
|
||||||
|
spathi 00200007 spathi
|
||||||
|
spathi 00400107 spahome
|
||||||
|
supox 00200007 supox
|
||||||
|
syreen 00200007 syreen
|
||||||
|
talkpet 00200007 talkpet
|
||||||
|
thradd 00200007 thradd
|
||||||
|
umgah 00200007 umgah
|
||||||
|
urquan 00200007 urquan
|
||||||
|
utwig 00200007 utwig
|
||||||
|
vux 00200007 vux
|
||||||
|
yehat 00200007 yehat
|
||||||
|
yehat 00400107 rebel
|
||||||
|
zoqfot 00200007 zoqfot
|
||||||
|
EOF
|
||||||
|
|
||||||
Reference in New Issue
Block a user