File: cob_fixer.py

package info (click to toggle)
spring 103.0%2Bdfsg2-1
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 43,720 kB
  • ctags: 63,685
  • sloc: cpp: 368,283; ansic: 33,988; python: 12,417; java: 12,203; awk: 5,879; sh: 1,846; xml: 655; perl: 405; php: 211; objc: 194; makefile: 77; sed: 2
file content (87 lines) | stat: -rwxr-xr-x 2,695 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#!/usr/bin/env python2

# This file is part of the Spring engine (GPL v2 or later), see LICENSE.html
# written by ashdnazg to help convert unit scripts after 4785bdcc
import sys
import struct
import glob
import os.path

COB_HEADER_FORMAT = "<13L"
COB_HEADER_FIELDS = (
	"VersionSignature",
	"NumberOfScripts",
	"NumberOfPieces",
	"TotalScriptLen",
	"NumberOfStaticVars",
	"Unknown_2",
	"OffsetToScriptCodeIndexArray",
	"OffsetToScriptNameOffsetArray",
	"OffsetToPieceNameOffsetArray",
	"OffsetToScriptCode",
	"Unknown_3",
	"OffsetToSoundNameArray",
	"NumberOfSounds"
)

OLD_RETURN = struct.pack("<3L", 0x10021001, 0, 0x10065000)
BAD_RETURN = struct.pack("<2L", 0x10022000, 0x10065000)
NEW_RETURN = struct.pack("<3L", 0x10021002, 1, 0x10065000)

def unpack(bytes, offset, format):
	return struct.unpack(format, bytes[offset:offset+struct.calcsize(format)])

def unpack_string(bytes, offset):
	b = bytes[offset:]
	return b[:b.index('\0')]

def process_cob(path):
	print("processing %s" % (path,))
	content = open(path, "rb").read()
	header_values = unpack(content, 0, COB_HEADER_FORMAT)
	header = dict(zip(COB_HEADER_FIELDS, header_values))

	name_offset_array = header["OffsetToScriptNameOffsetArray"]
	code_offset_array = header["OffsetToScriptCodeIndexArray"]

	script_names = []
	script_code_offsets = []
	for i in range(header["NumberOfScripts"]):
		(script_name_offset,) = unpack(content, name_offset_array + i * 4, "<L")
		(script_code_offset,) = unpack(content, code_offset_array + i * 4, "<L")
		script_names.append(unpack_string(content, script_name_offset))
		script_code_offsets.append(script_code_offset)

	script_code_offsets.append(header["TotalScriptLen"])

	if "Killed" not in script_names:
		return
	killed_index = script_names.index("Killed")
	killed_offset = script_code_offsets[killed_index] * 4
	killed_len = script_code_offsets[killed_index + 1] * 4 - killed_offset
	base_offset = header["OffsetToScriptCode"]
	killed_content = content[base_offset + killed_offset:base_offset + killed_offset + killed_len]
	killed_new_content = killed_content.replace(OLD_RETURN, NEW_RETURN)
	if BAD_RETURN in killed_new_content:
		print "Warning: need to fix script %s manually" % (path,)

	if killed_content == killed_new_content:
		return

	new_content = content[:base_offset + killed_offset] + killed_new_content + content[base_offset + killed_offset + killed_len:]
	open(path, "wb").write(new_content)



def main(root_folder):
	cob_paths = glob.glob(os.path.join(root_folder, "*.[Cc][Oo][Bb]"))
	for cob_path in cob_paths:
		process_cob(cob_path)


if __name__ == '__main__':
	if len(sys.argv) < 2:
		print("Usage: %s <cob root folder>" % (sys.argv[0],))
		exit(1)
	main(sys.argv[1])