Skip to content

Instantly share code, notes, and snippets.

@svanellewee
Last active May 6, 2016 23:19
Show Gist options
  • Save svanellewee/5c0b0059f5e593e47f7753ffced7b724 to your computer and use it in GitHub Desktop.
Save svanellewee/5c0b0059f5e593e47f7753ffced7b724 to your computer and use it in GitHub Desktop.
Wad reader
import sys
import logging
import struct
logging.basicConfig(level=logging.DEBUG)
logger =logging.getLogger(__name__)
def get_int(data):
return struct.unpack("<I", data)[0]
DIR_HEADER_SIZE=12
def get_header(data_stream):
retval = {}
header = data_stream.read(DIR_HEADER_SIZE)
retval.update(dict(wad_type = header[:4]))
retval.update(dict(num_entries = get_int(header[4:8])))
retval.update(dict(directory_absolute_location = get_int(header[8:DIR_HEADER_SIZE])))
retval.update(dict(data_size = retval['directory_absolute_location'] - DIR_HEADER_SIZE))
return retval
def get_data(data_stream, directory_location):
return { "data": data_stream.read(directory_location)}
def get_directory_entries(data_stream):
while True:
retval = {}
item = data_stream.read(16)
if len(item) != 16:
raise StopIteration
retval.update(dict(start = get_int(item[:4])))
retval.update(dict(size = get_int(item[4:8])))
retval.update(dict(name = item[8:16]))
yield retval
def build_directory(file_name):
with open(file_name, 'r') as wad_file:
header = get_header(wad_file)
wad_file.seek(header['directory_absolute_location'])
return [ i for i in get_directory_entries(wad_file)]
directory = build_directory(sys.argv[1])
print len(directory)
def extract_data(file_name, directory, lumps_of_interest=None):
retval = {}
with open(file_name, 'r') as wad_file:
for lump in directory:
if lumps_of_interest and lump['start'] not in lumps_of_interest:
continue
wad_file.seek(lump['start'])
data = wad_file.read(lump['size'])
#print lump, len(data)
retval[lump['name'].strip('\0')] = data
return retval
E1M1_lumps = [ 67500, 68880, 75532, 94972, 96840, 105624, 106572, 113180, 115392, 116296 ]
data = extract_data(sys.argv[1], directory, E1M1_lumps)
for key, value in data.items():
print key, len(value)
import io
def get_vertexes(byte_stream):
while True:
byte_elem = byte_stream.read(4)
if len(byte_elem) != 4:
raise StopIteration
yield struct.unpack("<hh", byte_elem)
def get_linedefs(byte_stream):
while True:
byte_elem = byte_stream.read(14)
if len(byte_elem) != 14:
raise StopIteration
retval = []
#yield [ ord(i) for i in byte_elem] # 16 bytes
with io.BytesIO(byte_elem) as b:
retval.append(struct.unpack("<hh", b.read(4)))
# ignore the rest
yield retval
# a = struct.unpack("<hh", byte_elem[:4])
# a = struct.unpack("<hh", byte_elem[:4])
# a = struct.unpack("<hh", byte_elem[:4])
# a = struct.unpack("<hh", byte_elem[:4])
def get_dims(vertex_data):
lower_left = None
with io.BytesIO(vertex_data) as bio:
low = min(v[0] for v in get_vertexes(bio))
bio.seek(0)
left = min(v[1] for v in get_vertexes(bio))
bio.seek(0)
high = max(v[0] for v in get_vertexes(bio))
bio.seek(0)
right = max(v[1] for v in get_vertexes(bio))
lower_left = (low, left)
upper_right = (high, right)
return lower_left,upper_right
lower_left, upper_right = get_dims(data['VERTEXES'])
import svgwrite
dwg = svgwrite.Drawing('test.svg', size = (abs(upper_right[0]), abs(upper_right[1])))
verts = []
with io.BytesIO(data['VERTEXES']) as bio:
for v in get_vertexes(bio):
x, y = (v[0] - lower_left[0])/10.0, (v[1] - lower_left[1])/10.0
verts.append((x,y))
dwg.add(svgwrite.shapes.Rect((x,y),size=(0.5,0.5), stroke='#333', stroke_width=3))
print len(verts),'LENG!'
count = 0
with io.BytesIO(data['LINEDEFS']) as bio:
for v in get_linedefs(bio):
count += 1
a,b = v[0]
print v, verts[a], verts[b]
dwg.add(dwg.line(verts[a],verts[b],stroke='#333', stroke_width=1))
print count
dwg.save()
# some node fun.
with io.BytesIO(data['NODES']) as nodedata:
idx = 0
while True:
dat = nodedata.read(8)
if not dat:
break
print idx,struct.unpack("<hhhh", dat)
idx +=1
nodedata.read(16)
nodedata.read(4)
import sys
import logging
import struct
logging.basicConfig(level=logging.DEBUG)
logger =logging.getLogger(__name__)
# def getbytes(data):
# return struct.unpack('<I', data)[0]
DIR_HEADER_SIZE=12
def get_directory(file_stream):
'''
0x00-0x03 string "PWAD" or "IWAD", defines whether the WAD is a PWAD or an IWAD
0x04-0x07 An integer specifying the number of entries in the directory
0x08-0x0b An integer holding a pointer to the location of the directory
'''
retval = {}
directory_raw = file_stream.read(DIR_HEADER_SIZE)
retval.update({"wad_type": directory_raw[:4]})
retval.update({"num_entries": getbytes(directory_raw[4:8])[0]})
retval.update({"location": getbytes(directory_raw[8:12])[0]})
data_size = retval['location'] - DIR_HEADER_SIZE
retval.update({'data': file_stream.read(data_size)})
return retval
def get_lump(file_stream):
lump = file_stream.read(16)
if len(lump) != 16:
return None
retval = {}
retval.update({"start": getbytes(lump[0:4])[0] - DIR_HEADER_SIZE})
retval.update({"size": getbytes(lump[4:8])[0]})
name = lump[8:16]
retval.update({"name": name})
return retval
def getbytes(data):
return struct.unpack("<I", data)
with open(sys.argv[1],'rb') as wadfile:
directory_data = get_directory(wadfile)
print directory_data['wad_type'], directory_data['location'], directory_data['num_entries'], len(directory_data['data'])
while True:
lump = get_lump(wadfile)
if not lump:
break
data = directory_data['data'][lump['start']:lump['start']+lump['size']]
print lump, len(data)
if lump['name'] == 'VERTEXES':
print struct.unpack("<hh", data[0:4])
print struct.unpack("<hh", data[4:8])
# print struct.unpack("<hh", data[0:4])
# print struct.unpack("<hh", data[0:4])
# lump_start = getbytes(data[0:4])
# lump_size = getbytes(data[4:8])
# lump_name = data[8:16]
@svanellewee
Copy link
Author

Rewrite of https://gist.github.com/jasonsperske/42284303cf6a7ef19dc3
and using http://zdoom.org/wiki/WAD as spec...

(just because I'm stupid and need things spelt out)

@svanellewee
Copy link
Author

(incomplete)

@svanellewee
Copy link
Author

So it looks like WAD is a bit arse backwards, perhaps I'm wrong ?

The layout seems to be :

  • header
  • body
  • index (dictionary)

Though my get_lump code is the thing that actually get's the index entries so that we can get the right stuff inside the body(ie the previous element)

I guess sqlite wasn't invented yet.

@svanellewee
Copy link
Author

svanellewee commented Apr 26, 2016

get_linedefs gets linedef packets of 14 (doom version) and only uses the first 4 bytes (start vertex index -end vertex index)

Code then ignores the rest for now

From http://zdoom.org/wiki/Linedef... read it as follows : 0-1 means byte 0 AND byte 1, 2-3 means byte 2 AND byte 3 etc.
Bytes Data type Description
0-1 Unsigned short Beginning vertex
2-3 Unsigned short Ending vertex
4-5 Unsigned short Flags
6-7 Unsigned short Line type
8-9 Unsigned short Sector tag
10-11 Unsigned short Right sidedef - 0xFFFF means there is no sidedef
12-13 Unsigned short Left sidedef - 0xFFFF means there is no sidedef

@svanellewee
Copy link
Author

Rendered file:
test

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment