Skip to content

Instantly share code, notes, and snippets.

@alexbevi
Created March 6, 2010 13:56
Show Gist options
  • Save alexbevi/323697 to your computer and use it in GitHub Desktop.
Save alexbevi/323697 to your computer and use it in GitHub Desktop.
#!/usr/bin/python
#
# Strings Extractor for Draci Historie
#
# usage:
# run this script from the same directory as the
# RETEZCE.DFW file. It will create a RETEZCE.TXT file
# that can be edited in any text editor
#
from struct import *
from os import path
from sys import exit
# Target archive with the Draci Historie text strings
BASE_FILENAME = "RETEZCE.DFW"
DUMP_FILENAME = "RETEZCE.TXT"
# Read a byte off the filestream if no target specified. If a target
# is given, interpret it as a numeric value
def read(target = None):
value = target if target != None else infile.read(1)
return unpack("B", value)[0]
# Read 2-bytes (little-endian) off the filestream
def read16():
value = infile.read(2)
return unpack("<H", value)[0]
# Read 4-bytes (little-endian) off the filestream
def read32():
value = infile.read(4)
return unpack("<L", value)[0]
# Ensure that the file is in the same location as the
# text extraction script
if not path.exists(BASE_FILENAME):
print BASE_FILENAME + " not found"
exit()
print "Found file [" + BASE_FILENAME + "]"
infile = open(BASE_FILENAME, "r")
# Read in the archive and record information
header = infile.read(4)
entries = read16()
footer_offset = read32()
infile.seek(footer_offset)
offsets = []
for i in range(0,entries):
offsets.append(read32())
outfile = open(DUMP_FILENAME, "wb")
for j in range(0, entries):
infile.seek(offsets[j])
comp_len = read16() # string length + header size (6)
length = read16() # string length
comp_type = read() # always zero
data_crc = read()
# XXX The final byte seems to indicate string length, but isn't
# used directly (though it could be)
#string_len = read()
# Read the string in, but ignore the final character, as it's
# always a pipe
data = infile.read(length)
crc = 0
for k in range(0, length):
crc ^= read(data[k])
if crc != data_crc:
print "CRC Mismatch for entry #" + j
infile.close()
exit
# Note that our output needs to make the following adjustments
# (a) start from start + 1 as that byte, which contains the length
# of the string, is unused when extracting the string, but is
# used for CRC calculation
# (b) drop the last character, which is always a pipe (|)
# (c) add a line break to the end of each record
outfile.write(data[1:len(data)-1] + "\n")
print "Dumped strings to [" + DUMP_FILENAME + "]"
outfile.close()
infile.close()
#!/usr/bin/python
#
# Strings Builder for Draci Historie
#
# usage:
# run this script from the same directory as the
# RETEZCE.TXT file (which was created with draci_strings_extract.py)
# It will build a RETEZCE.DFW.NEW file that can then be used
# with Draci Historie in ScummVM.
# This script appends a .NEW extension to the generated file just in
# case it has been run from the Draci Historie location, and doen't
# assume that you want to overwrite the original.
#
from struct import pack, unpack
from os import path
from sys import exit
# Target archive with the Draci Historie text strings
BASE_FILENAME = "RETEZCE.TXT"
DUMP_FILENAME = "RETEZCE.DFW.NEW"
# Return the given value as numeric
def read(target):
return unpack("B", target)[0]
# Read a byte off the filestream if no target specified. If a target
# is given, interpret it as a numeric value
def w(target):
return pack("B", target)[0]
# Read 2-bytes (little-endian) off the filestream
def w16(target):
return pack("<H", target)
# Read 4-bytes (little-endian) off the filestream
def w32(target):
return pack("<L", target)
# Generate a simple CRC value
def generate_crc(data):
crc = 0
for i in range(0, len(data)):
crc ^= read(data[i])
return crc
# Ensure that the file is in the same location as the
# strings builder script
if not path.exists(BASE_FILENAME):
print BASE_FILENAME + " not found"
exit()
print "Found file [" + BASE_FILENAME + "]"
infile = open(BASE_FILENAME, "r")
entries = []
# Read each line into an array
for line in infile:
entries.append(line)
infile.close()
outfile = open(DUMP_FILENAME, "wb")
outfile.write("BAR!")
outfile.write(w16(len(entries)))
outfile.write(w32(0)) # Placeholder for footer location
offsets = []
for entry in entries:
entry = entry[0:len(entry) - 1] # Remove the line break
entry += "|" # Each entry is terminated by a pipe
length = len(entry)
entry = w(length) + entry
comp_len = length + 7
outfile.write(w16(comp_len)) # Compressed Length
outfile.write(w16(length + 1)) # Entry Length
outfile.write(w(0)) # Compression Type (always zero)
crc = generate_crc(entry)
outfile.write(w(crc))
outfile.write(entry)
offsets.append(comp_len)
pos = 10
for offset in offsets:
outfile.write(w32(pos))
pos += offset
outfile.write(w32(pos)) # Final entry is footer location
outfile.seek(6)
outfile.write(w32(pos)) # Write foother location in header
print "Built strings file [" + DUMP_FILENAME + "]"
outfile.close()
Just so you know, running an md5 sum on the two files (with no alteration to the generated text file) should yield identical results ;)
alex@home:~$ md5sum RETEZCE.DFW RETEZCE.DFW.NEW
6127b1f6e0cfc23bb1a42959063034d8 RETEZCE.DFW
6127b1f6e0cfc23bb1a42959063034d8 RETEZCE.DFW.NEW
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment