forked from Chupalika/Kaleo
-
Notifications
You must be signed in to change notification settings - Fork 0
/
bindata.py
executable file
·128 lines (98 loc) · 4.37 KB
/
bindata.py
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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#!/usr/bin/python
import sys
from os import chdir, getcwd
from struct import unpack
class BinStorage:
workingdirs = {}
pathUsed = None
def __init__(self, binFile, source="ext"):
#change path to working path if this is the first bin file being read - this change sticks and only needs to be done once per program run
if source != BinStorage.pathUsed:
chdir(BinStorage.workingdirs[source])
BinStorage.pathUsed = source
try:
with open(binFile, "rb") as file:
self.contents = file.read()
except IOError:
sys.stderr.write("Couldn't open the file {}. Please check the file is present.\n".format(self.workingdirs[source]+"/"+binFile))
sys.exit(1)
self.num_records = unpack("<I",self.contents[0:4])[0]
self.record_len = unpack("<I",self.contents[4:8])[0] #Len of a single data "row"
# 1st section - Text
self.text_start_point = unpack("<I",self.contents[8:12])[0]
self.text_len = unpack("<I",self.contents[12:16])[0] #Total len of the text section
# 2nd section - Data
self.data_start_point = unpack("<I",self.contents[16:20])[0]
self.data_len = unpack("<I",self.contents[20:24])[0] #Total len of the data section, effectively num_records * record_len
# 3rd section - Unknown, looks like an index
self.third_seg_start_point = unpack("<I",self.contents[24:28])[0]
self.third_seg_len = unpack("<I",self.contents[28:32])[0] #Total len of the 3rd section (4 bytes * num_records)
# 4th section - Unknown
self.fourth_seg_start_point = unpack("<I",self.contents[32:36])[0]
self.fourth_seg_len = unpack("<I",self.contents[36:40])[0]
self.text_only_len = unpack("<I",self.contents[52:56])[0]
#length of text segment sans file internal ID. Used for message bins.
def getAll(self):
return self.contents
def getRecord(self,index):
record_start = self.data_start_point + index * self.record_len
return self.contents[record_start:record_start+self.record_len]
def getHeader(self):
return self.contents[:self.text_start_point]
def getTextSegment(self):
return self.contents[self.text_start_point:self.text_start_point+self.text_len]
def getMessage(self, index):
#this gets a message from a message .bin. Important: IT ONLY WORKS ON MESSAGE BINS - their data segment is an index for their text segment. If you try it on any other .bin, expect gibberish.
if index >= self.num_records:
raise IndexError
messageStart = unpack("<I", self.getRecord(index))[0]
if index == self.num_records - 1:
#last message:
message = self.contents[messageStart:self.text_start_point+self.text_only_len-5]
#the 5 is for the string 'data ' that sits after text but before name and data
else:
nextMessageStart = unpack("<I", self.getRecord(index+1))[0]
message = self.contents[messageStart:nextMessageStart]
# the binary string seems to be utf-16 little indian
utf16 = message.decode('utf-16-le', 'replace')
# convert to utf-8 for handling in python
utf8 = utf16.encode('utf-8', 'replace')
return utf8
def getAllRecords(self):
return self.contents[self.data_start_point:self.third_start_point]
def getThirdSegment(self):
return self.contents[self.third_start_point:]
#a few more binary utilities...
#Reads a certain number of bits starting from an offset byte and bit and returns the value
def readbits(text, offsetbyte, offsetbit, numbits):
ans = ""
bytes = [ord(b) for b in text[offsetbyte:offsetbyte+4]]
val = 0
for i in reversed(xrange(4 if len(bytes) > 4 else len(bytes))):
val *= 256
val += bytes[i]
val >>= offsetbit
val &= (1 << numbits) -1
return val
def readbyte(text, offsetbyte):
return ord(text[offsetbyte])
def readfloat(text, startbyte, roundTo=2): #expects a 4 byte float
return round(unpack("f", text[startbyte:startbyte+4])[0],roundTo)
#Checks the first 2 bytes of a file and returns the value
def getnumentries(filename):
file = open(filename, "rb")
contents = file.read()
numentries = readbits(contents, 0, 0, 32)
file.close()
return numentries
#Defines the global list for pokemon abilities and descriptions
def definepokemonabilitylist():
try:
listfile = open("pokemonabilitylist.txt", "r")
thewholething2 = listfile.read()
global pokemonabilitylist
pokemonabilitylist = thewholething2.split("\n")
listfile.close()
except IOError:
sys.stderr.write("Couldn't find pokemonabilitylist.txt to retrieve Pokemon abilities.\n")
pokemonabilitylist = [""] #to prevent calling this function again