diff --git a/collate.py b/collate.py deleted file mode 100644 index 26c21fc..0000000 --- a/collate.py +++ /dev/null @@ -1,26 +0,0 @@ -import os - -root_dir = '.\output' -output = {} - -for directory, subdirectories, files in os.walk(root_dir): - for file in files: - if directory == root_dir: - with open(directory + '\\' + file, 'r') as f: - print '-'+file - text = f.readlines() - for lines in text: - index = 0 - while lines[index] != ',': - index+=1 - if lines[-1:] != '\n': - lines += '\n' - output[int(lines[:index])] = lines[index + 1:] - - with open('collated.txt', 'w') as fileOutput: - textOutput = '' - for params in range(output, keys=int): - if params in output: - textOutput += str(params) + ':"' + output[params][:-1] + '",\n' - fileOutput.write(textOutput) - print "done" diff --git a/convert.py b/convert.py deleted file mode 100644 index 1221598..0000000 --- a/convert.py +++ /dev/null @@ -1,21 +0,0 @@ -import os -from src import decoder - -root_dir = '.\devices' -folder = {0:'.\devices', - 1:'.\devices\old devices'} - -decoder.mode = 3 -print ("Heyo I'm Jaxter, thanks for using my little script. Notice how I said 'my'. That's right, this is something that I made, and you should therefore be pretty careful about using it. I won't take responsibility for breaking your Bitwig devices, so make sure you have a copy of them saved somewhere before starting. Also, be cautious in general; just because I trust my own work doesn't mean you should.\nNow, with the fearmongering out of the way, lets get started.\n") -try: - inputDir = input("Enter the subdirectory that your devices are in. (If this script is in the same folder as them, just press enter)") -except SyntaxError: - inputDir = '' -inputDir = '.' + inputDir -if not os.path.exists('output'): - os.mkdir('output') -for file in os.listdir(inputDir): - if file.endswith(".bwdevice") or file.endswith(".bwproject") or file.endswith(".bwmodulator") or file.endswith(".bwpreset") or file.endswith(".bwclip") or file.endswith(".bwscene"): - print ('-'+file) - decoder.magic(file, inputDir) -input("All done. Let me know if you had any issues using this or have suggestions for improving it. [Press Enter to exit]") \ No newline at end of file diff --git a/editor.py b/editor.py deleted file mode 100644 index 3685da6..0000000 --- a/editor.py +++ /dev/null @@ -1,377 +0,0 @@ -from src import decoder -import os -from kivy.app import App -from kivy.uix.floatlayout import FloatLayout -from kivy.uix.gridlayout import GridLayout -from kivy.uix.label import Label -from kivy.uix.textinput import TextInput -from kivy.uix.button import Button -from kivy.uix.dropdown import DropDown -from kivy.uix.button import Button -from kivy.uix.scrollview import ScrollView -from kivy.core.window import Window -from kivy.uix.behaviors import DragBehavior -from kivy.uix.screenmanager import ScreenManager, Screen -from kivy.base import runTouchApp - -from kivy.graphics import * -height = 2000 -mult = 2 - -class DragButt(DragBehavior, Label): - pass - -def drawConn(canv,objLocs,objSize, fr,to, inport = 0,outport = 0): #fr is short for from - x1 = mult*objLocs[fr][0] + inport*10 + 10 - y1 = height-objLocs[fr][1] - if (objLocs[to][0] == -1 and objLocs[to][1] == -1): - x2 = x1 - y2 = y1 +10 - else: - x2 = mult*objLocs[to][0] + outport*10 + 10 - y2 = height-objLocs[to][1]-objSize[to][1] - diff = abs(y1-y2)/2 - with canv.canvas: - Color(1., 1., 1.) - Line(bezier=(x1,y1,x1,y1+diff,x2,y2-diff,x2,y2)) - -def addAtom(canv,objLocs,objSize, obj): - x = mult*obj.fields["settings(6194)"].fields["desktop_settings(612)"].fields["x(17)"] - y = height - obj.fields["settings(6194)"].fields["desktop_settings(612)"].fields["y(18)"] - flag = 0 - name = '' - if (len(objLocs) < obj.id): - for i in range(obj.id-len(objLocs) + 1): - objLocs.append((-1,-1)) - objSize.append((-1,-1)) - nameIn = obj.classname - #values - if nameIn == 'float_core.decimal_value_atom(289)': - name = obj.fields["name(374)"] - val = obj.fields["value_type(702)"].fields["default_value(891)"] - width = 15 + 6*len(name) - name += '\n' + str(val)[:6] - objSize[obj.id] = (width,40) - flag = 1 - elif nameIn == 'float_core.boolean_value_atom(87)': - name = obj.fields["name(374)"] - val = obj.fields["value_type(702)"].fields["default_value(6957)"] - width = 15 + 6*len(name) - name += '\n' + str(val) - objSize[obj.id] = (width,40) - flag = 1 - elif nameIn == 'float_core.indexed_value_atom(180)': - name = obj.fields["name(374)"] - width = 15 + 6*len(name) - vals = obj.fields["value_type(702)"].fields["items(393)"] - dropdown = DropDown() - i = 0 - for val in vals: - btn = Button(text=val.fields["name(651)"], font_size='12sp', size_hint_y=None, height=20) - btn.bind(on_release=lambda btn: dropdown.select(btn.text)) - dropdown.add_widget(btn) - i += 1 - objSize[obj.id] = (width,40) - mainbutton = Button(text=name + '\n' + vals[0].fields["name(651)"], font_size='12sp', pos=(x,y-40), size_hint=(None, None), size=objSize[obj.id]) - mainbutton.bind(on_release=dropdown.open) - dropdown.bind(on_select=lambda instance, x: setattr(mainbutton, 'text', name + '\n' + x)) - canv.add_widget(mainbutton) - elif nameIn == 'float_core.integer_value_atom(394)': - name = obj.fields["name(374)"] - val = obj.fields["value_type(702)"].fields["default_value(6956)"] - width = 15 + 6*len(name) - name += '\n' + str(val) - objSize[obj.id] = (width,40) - flag = 1 - elif nameIn == 'float_common_atoms.constant_value_atom(314)': - name = str(obj.fields["constant_value(750)"])[:5] - width = 15 + 6*len(name) - objSize[obj.id] = (width,40) - flag = 1 - elif nameIn == 'float_common_atoms.constant_integer_value_atom(298)': - name = str(obj.fields["constant_value(720)"]) - width = 15 + 6*len(name) - objSize[obj.id] = (width,40) - flag = 1 - #atoms - elif nameIn == 'float_common_atoms.nitro_atom(1721)': - name = 'nitro' - val = obj.fields["code(6264)"][:19] - width = 15 + 6*len(val) - name += '\n' + str(val) - objSize[obj.id] = (width,40) - flag = 1 - elif nameIn == 'float_common_atoms.mix_atom(301)': - name = 'MIX' - objSize[obj.id] = (30,30) - flag = 1 - elif nameIn == 'float_common_atoms.polyphonic_note_voice_atom(350)': - name = 'PolyNoteVoice' - objSize[obj.id] = (90,30) - flag = 1 - elif nameIn == 'float_common_atoms.note_delay_compensation_atom(1435)': - name = 'NoYo' - objSize[obj.id] = (30,30) - flag = 1 - elif nameIn == 'float_common_atoms.delay_compensation_atom(1371)': - name = 'oYo' - objSize[obj.id] = (30,30) - flag = 1 - elif nameIn == 'float_core.modulation_source_atom(766)': - name = obj.fields["name(3639)"] + '\no->' - width = 15 + 6*(len(name)-4) - objSize[obj.id] = (width,30) - flag = 1 - elif nameIn == 'float_core.value_led_atom(189)': - name = 'led' - objSize[obj.id] = (20,20) - flag = 1 - #other atoms - elif nameIn == 'float_common_atoms.decimal_event_filter_atom(400)': - name = 'decFilter' - val1 = obj.fields["comparison(842)"] - val2 = obj.fields["comparison_value(843)"] - width = 15 + 6*len(name) - name += '\n' + str(val1) + '\n' + str(val2) - objSize[obj.id] = (width,60) - flag = 1 - elif nameIn == 'float_common_atoms.multiplexer_atom(1188)': - name = 'MUX' - val = obj.fields["inputs(4763)"] - objSize[obj.id] = (10*val+20,30) - name += ' ' + str(val) - flag = 1 - elif nameIn == 'float_common_atoms.deinterleave_atom(368)': - name = 'L/R' - objSize[obj.id] = (30,30) - flag = 1 - elif nameIn == 'float_common_atoms.indexed_lookup_table_atom(344)': - name = 'lookup' - vals = obj.fields["row_data(744)"] - length = obj.fields["row_count(743)"] - name += '\n' - for i in range(length): - name += str(vals[i].fields["cells(726)"][0].fields["value(739)"]) + '|' - width = 6*(len(name)-6) - objSize[obj.id] = (width,40) - flag = 1 - #math - elif nameIn == 'float_common_atoms.constant_add_atom(308)': - name = '+' - val = obj.fields["constant_value(750)"] - name += '\n' + str(val) - objSize[obj.id] = (40,40) - flag = 1 - elif nameIn == 'float_common_atoms.constant_multiply_atom(303)': - name = 'x' - val = obj.fields["constant_value(750)"] - name += '\n' + str(val)[:5] - objSize[obj.id] = (40,40) - flag = 1 - elif nameIn == 'float_common_atoms.add_atom(337)': - name = '+' - objSize[obj.id] = (30,30) - flag = 1 - elif nameIn == 'float_common_atoms.multiply_atom(367)': - name = 'x' - objSize[obj.id] = (30,30) - flag = 1 - elif nameIn == 'float_common_atoms.subtract_atom(343)': - name = '-' - objSize[obj.id] = (30,30) - flag = 1 - elif nameIn == 'float_common_atoms.multiply_add_atom(304)': - name = 'x+' - objSize[obj.id] = (obj.fields["multiplier_pairs(724)"]*20+10,30) - flag = 1 - elif nameIn == 'float_common_atoms.sum_atom(305)': - name = '+++' - val = obj.fields["inputs(725)"] - name += str(val) - objSize[obj.id] = (val*10+10,40) - flag = 1 - #DSP - elif nameIn == 'float_common_atoms.svf_filter_atom(578)': - name = 'filter' - objSize[obj.id] = (30,30) - flag = 1 - elif nameIn == 'float_common_atoms.surge_classic_oscillator_atom(491)': - name = 'surge osc' - objSize[obj.id] = (100,30) - flag = 1 - #components - elif nameIn == 'float_core.proxy_in_port_component(154)': - name = 'IN' - #val = obj.fields["port(301)"].fields["decorated_name(499)"] - #name += '\n' + val - objSize[obj.id] = (30,30) - flag = 1 - elif nameIn == 'float_core.proxy_out_port_component(50)': - name = 'OUT' - #val = obj.fields["port(301)"].fields["decorated_name(499)"] - #name += '\n' + val - objSize[obj.id] = (30,30) - y-=300 - flag = 1 - elif nameIn == 'float_core.nested_device_chain_slot(587)': - name = 'nest' - val = obj.fields["name(835)"] - name += '\n' + val - objSize[obj.id] = (40,40) - flag = 1 - #everything else - else: - dot = 0 - paren = 0 - name = '' - num = '' - for i in range(len(nameIn)): - if nameIn[i] == '(': - paren = 1 - if dot and not paren: - if nameIn[i] == '_': - name += ' ' - else: - name += nameIn[i] - elif paren: - num += nameIn[i] - if nameIn[i] == '.': - dot = 1 - if not name: - name = num - width = 8*len(name) + 16 - objSize[obj.id] = (width,30) - flag = 1 - if flag: - #canv.add_widget(DragButt(text=name, pos = (x,y-objSize[obj.id][1]), font_size='12sp', size_hint=(None, None), size=objSize[obj.id], drag_rectangle=(x, y, objSize[obj.id][0], objSize[obj.id][1]), drag_timeout=10000000, drag_distance=0)) - canv.add_widget(Button(text=name, pos = (x,y-objSize[obj.id][1]), font_size='12sp', size_hint=(None, None), size=objSize[obj.id])) - objLocs[obj.id] = (x/mult,height-y) - -def setSlide(instance, manager, screenName): - manager.current = screenName - -def addKids(canv,objLocs,objSize, child): - try: - kids = child.fields["settings(6194)"].fields["inport_connections(614)"] - except AttributeError: - pass - #print(child) - except: - pass - #print(child.fields) - else: - item = 0 - while(item < len(kids)): - try: - kids[item].fields["source_component(248)"].fields["settings(6194)"].fields["desktop_settings(612)"].fields["x(17)"] - except AttributeError: #its a reference - pass - if kids[item].fields["source_component(248)"]: - drawConn(canv,objLocs,objSize, child.id, kids[item].fields["source_component(248)"].classNum, item, kids[item].fields["outport_index(249)"]) - except: - pass - #print(kids[item].fields) - else: - addAtom(canv,objLocs,objSize,kids[item].fields["source_component(248)"]) - drawConn(canv,objLocs,objSize, child.id, kids[item].fields["source_component(248)"].id, item, kids[item].fields["outport_index(249)"]) - addKids(canv,objLocs,objSize,kids[item].fields["source_component(248)"]) - item += 1 - -class Hello(GridLayout): - def __init__(self,**kwargs): - global sm, fileNames - self.rows = 2 - super(Hello,self).__init__(**kwargs) - sm = ScreenManager(size_hint = (1, 9)) - selector = GridLayout(rows=1, size_hint = (1,1)) - fileNames = {} - for file in os.listdir('.'): - opened = [] - if file.endswith(".bwdevice") or file.endswith(".bwproject") or file.endswith(".bwmodulator") or file.endswith(".bwpreset") or file.endswith(".bwclip") or file.endswith(".bwscene"): - print ('-'+file) - with open(file, 'rb') as readThis: - opened = decoder.objectify(readThis.read()) - subWidget = FloatLayout(size_hint=(None,None), size = (5000,height)) - objLocs = [] - objSize = [] - for eachField in ("child_components(173)","proxy_in_ports(177)","proxy_out_ports(178)"): - children = opened[1].fields[eachField] - item = 0 - while(item < len(children)): - try: - children[item].fields["settings(6194)"].fields["desktop_settings(612)"].fields["x(17)"] - except AttributeError: #its a reference - pass - #print(children[item]) - except: - pass - #print(children[item].fields) - else: - addAtom(subWidget,objLocs,objSize, children[item]) - addKids(subWidget,objLocs,objSize, children[item]) - item += 1 - screen = Screen(name=file) - root = ScrollView(size_hint=(1, 1)) - root.add_widget(subWidget) - screen.add_widget(root) - #screen.add_widget(subWidget) - sm.add_widget(screen) - but = Button(text=file[:-9], font_size='8sp', on_press=self.callback) - fileNames[but] = file - selector.add_widget(but) - #selector.add_widget(Button(text=file)) - sm.current = "Peak Limiter.bwdevice" - self.add_widget(selector) - self.add_widget(sm) - - def callback(instance, value): - print('My button <%s> state is <%s>' % (instance, value)) - global sm, fileNames - sm.current = fileNames[value] - - def newScene(self, event): - global sm, fileName - sm.current = fileName - -class app1(App): - def build(self): - return Hello() -if __name__=="__main__": - app1().run() - - ''''sm = ScreenManager(size_hint = (1, 9)) - selector = GridLayout(rows=1, size_hint = (1,1)) - overall = GridLayout(rows=2) - for file in os.listdir('.'): - opened = [] - if file.endswith(".bwdevice") or file.endswith(".bwproject") or file.endswith(".bwmodulator") or file.endswith(".bwpreset") or file.endswith(".bwclip") or file.endswith(".bwscene"): - print ('-'+file) - with open(file, 'rb') as readThis: - opened = decoder.objectify(readThis.read()) - subWidget = FloatLayout(size_hint_y=None, size = (2000,height)) - for eachField in ("child_components(173)","proxy_in_ports(177)","proxy_out_ports(178)"): - children = opened[1].fields[eachField] - item = 0 - while(item < len(children)): - try: - children[item].fields["settings(6194)"].fields["desktop_settings(612)"].fields["x(17)"] - except AttributeError: #its a reference - pass - #print(children[item]) - except: - pass - #print(children[item].fields) - else: - addAtom(subWidget, children[item]) - addKids(subWidget, children[item]) - item += 1 - screen = Screen(name=file) - root = ScrollView(size_hint=(1, 1)) - root.add_widget(subWidget) - screen.add_widget(root) - sm.add_widget(screen) - selector.add_widget(Button(text=file, on_press=newScene(name=file))) - sm.current = "Peak Limiter.bwdevice" - overall.add_widget(selector) - overall.add_widget(sm) - runTouchApp(overall)''' \ No newline at end of file diff --git a/input/Default.bwpreset b/input/Default.bwpreset new file mode 100644 index 0000000..a16507b Binary files /dev/null and b/input/Default.bwpreset differ diff --git a/input/Default.bwremotecontrols b/input/Default.bwremotecontrols new file mode 100644 index 0000000..e0679b4 Binary files /dev/null and b/input/Default.bwremotecontrols differ diff --git a/input/isqa remake attempt.bwproject b/input/isqa remake attempt.bwproject new file mode 100644 index 0000000..00c4b33 Binary files /dev/null and b/input/isqa remake attempt.bwproject differ diff --git a/input/test.bwclip b/input/test.bwclip new file mode 100644 index 0000000..30a25cc Binary files /dev/null and b/input/test.bwclip differ diff --git a/input/testPreset.bwpreset b/input/testPreset.bwpreset new file mode 100644 index 0000000..c94ceb5 Binary files /dev/null and b/input/testPreset.bwpreset differ diff --git a/input/testproj.bwproject b/input/testproj.bwproject new file mode 100644 index 0000000..a92838f Binary files /dev/null and b/input/testproj.bwproject differ diff --git a/main.py b/main.py new file mode 100644 index 0000000..a001736 --- /dev/null +++ b/main.py @@ -0,0 +1,126 @@ +import os +from src import decoder, encoder, extractor +from src.lib import fs, util, atoms +from src.lib.luts import typeLists + +root_dir = '.\devices' +folder = {0:'.\devices', + 1:'.\devices\old devices'} + +classExtract = 1 #change this to 0 for regular operation and 1 for class extraction. most users won't need class extraction, as it is just a tool I use to make internal files + +def magic(name, directory): #decodes then reencodes a single file + global extractedClasses, extractedFields + device_data = fs.read_binary(directory + '\\' + name) + header = device_data[:40].decode("utf-8") #'BtWg00010001008d000016a00000000000000000' + if (header[11] == '2'): + if (classExtract): + classes,fields = extractor.bwExtract(device_data) + extractedClasses.append(classes) + extractedFields.append(fields) + else: + header = header[:11] + '1' + header[12:] + output = '' + gather = decoder.bwDecode(device_data) + for item in gather: #encodes all of the objects in the output list + output += util.json_encode(atoms.serialize(item)) + output = header + decoder.reformat(output) + with open('output\\converted ' + name, 'wb') as file: + file.write(output.encode("utf-8")) + gather2 = encoder.bwEncode(gather) + header = header[:11] + '2' + header[12:] + output2 = header.encode('utf-8') + gather2 + with open('output\\reconverted ' + name, 'wb') as file: + file.write(output2) + #elif (device_data[42] == '{'): + # print("this file is either already converted or isn't an unreadable file") + elif (header[11] == '1'): + print("this is already converted") + else: + print("i don't know what kind of file this is") + +def x_in_y(query, base): + try: + l = len(query) + except TypeError: + l = 1 + query = type(base)((query,)) + + for i in range(len(base)): + if base[i:i+l] == query: + return True + return False + +def extractClasses(): + global extractedClasses, extractedFields + collatedClassDict = typeLists.classList + collatedFieldDict = typeLists.fieldList + #collatedClassListErrors = {} + for eachFile in extractedClasses: + for eachClass in eachFile: + if eachClass in collatedClassDict: + ###check to see if the class matches the existing class + #diffkeys = [k for k in collatedClassDict[eachClass] if collatedClassDict[eachClass][k] != eachFile[k]] + #if diffkeys: + if collatedClassDict[eachClass] != eachFile[eachClass]: + if set(eachFile[eachClass])>set(collatedClassDict[eachClass]): + collatedClassDict[eachClass] = eachFile[eachClass] + elif set(eachFile[eachClass]) 0): - field += chr(text[offset]) - offset+=1 - headerLength-=1 - field += '"' + if currentSection == 2: + offset += 57 + field = intConv(text[offset:offset+headerLength]) + offset += headerLength + elif headerLength<54: + print("short header at " + hex(offset)) + offset += 54 + stringMode = 1 + field = getClass(text) + stringMode = 0 + else: + field = intConv(text[offset:offset+headerLength]) + offset += headerLength return field elif parseType == 0x12: #object array tempList = [] while (intConv(text[offset:offset+4]) != 0x3): + if endFlag: + break tempList.append(getClass(text)) - #print (tempList) offset += 4 return tempList elif parseType == 0x14: #map string #field += 'type : "map",\n' + 'data :\n' + '{\n' object = atoms.Atom() + string = '' + mapping = atoms.Atom() parseType2 = (text[offset]) offset += 1 if parseType2 == 0x1: object.add_field("type", "map") - stringLength = intConv(text[offset:offset+4]) - offset += 4 - string = bigChr(text[offset:offset+stringLength]) - offset += stringLength - mapping = atoms.Atom() - object.add_field("type", "map") - mapping.add_field(string, getClass(text)) + stringLength = intConv(text[offset:offset+4]) + offset += 4 + string = bigChr(text[offset:offset+stringLength]) + offset += stringLength + mapping.add_field(string, getClass(text)) + offset+=1 + elif parseType2 == 0x0: + object.add_field("type", "map") + mapping.add_field('', None) + else: + object.add_field("type", "unknown") object.add_field("data", mapping) - offset+=1 return object - elif parseType == 0x15: #16 character hex value - for i in range(16): - if i in [4,6,8,10]: - field += '-' - field += "{0:0{1}x}".format((text[offset+i]),2) #includes leading zeros + elif parseType == 0x15: #UUID + value = str(uuid.UUID(bytes=text[offset:offset+16])) offset += 16 - return field + return value elif parseType == 0x16: #color flVals = [] flVals.append(struct.unpack('f', struct.pack('L', intConv(text[offset:offset + 4])))[0]) @@ -174,107 +181,89 @@ def parseField(text, stringType = 0): offset += 4 offset += arrayLength*4 return field - elif parseType == 0x19: #package reference array + elif parseType == 0x19: #string array arrayLength = intConv(text[offset:offset+4]) offset += 4 - field += '[' + arr = [] for i in range(arrayLength): - field += (chr(text[offset+i])) + ', ' - if arrayLength: - field = field[:-2] - field += ']' - offset += arrayLength + strLength = intConv(text[offset:offset+4]) + offset += 4 + arr.append(text[offset:offset+strLength].decode("utf-8")) + offset+=strLength + return arr + elif parseType == 0x1a: #fuck if i know #not done yet + #print("shit,1a ") + strLength = 0 + while (text[offset] == 0x00): + strLength = intConv(text[offset:offset+4]) + offset += 4 + if strLength == 0x90: + return '' + field = text[offset:offset+strLength].decode("utf-8") + offset += strLength return field + else: + endFlag = 1 print('unknown type at ' + hex(offset-1) + ', ' + str(parseType)) - return '' + return 'end here' def bigOrd(text): - output = 0 - for i in range(len(text)): - #print(len(text)-i - 1) - output += (256**(len(text)-i - 1))*(text[i]) - return output + output = 0 + for i in range(len(text)): + #print(len(text)-i - 1) + output += (256**(len(text)-i - 1))*(text[i]) + return output def bigChr(chain): - output = '' - for i in range(len(chain)): - #print(len(text)-i - 1) - output += chr(chain[i]) - return output + output = '' + for i in range(len(chain)): + #print(len(text)-i - 1) + output += chr(chain[i]) + return output def intConv(chain): - output = 0 - for i in range(len(chain)): - #print(len(chain)-i - 1) - output += (256**(len(chain)-i - 1))*chain[i] - return output - -def coords(text): - output = "" - lastStart = 0 - isX = 0 - findComma = 0 - for pick in range(14, len(text)): - if bigChr(text[pick - 10:pick]) == '\"x(17)\" : ': - lastStart = pick - isX = 1 - findComma = 1 - if bigChr(text[pick - 10:pick]) == '\"y(18)\" : ': - lastStart = pick - isX = 0 - findComma = 1 - if bigChr(text[pick - 14:pick]) == '\"name(374)\" : ': - lastStart = pick - isX = 0 - findComma = 1 - if bigChr(findComma and (text[pick]) == ','): - if isX: - output += '\n' - else: - output += ',' - findComma = 0 - output += bigChr(text[lastStart:pick]) - return output[1:] - -def settings(text): - output = "" - for pick in range(len(text)-5): - if bigChr(text[pick:pick + 5]) == ')\" : ': - preIndex = 0 - while bigChr(text[pick - preIndex]) != '(': - preIndex += 1 - number = bigChr(text[pick-preIndex + 1:pick]) - pIndexNum = preIndex - while chr(text[pick - preIndex]) != '\"': - preIndex += 1 - parameter = bigChr(text[pick-preIndex + 1:pick-pIndexNum]) - output += '\n' + number + ',' + parameter - return output[1:] - -def classes(text): - output = "" - for pick in range(len(text)): - if pick > 100: - if text[pick-9:pick] == 'class : \"': - preIndex = 0 - while text[pick + preIndex] != '(': - preIndex += 1 - parameter = text[pick:pick + preIndex] - '''if parameter[:11] == 'float_core.': - parameter = parameter[11:] - if parameter[:19] == 'float_common_atoms.': - parameter = parameter[19:]''' - pIndexNum = preIndex - while text[pick + preIndex] != ')': - preIndex += 1 - number = text[pick+pIndexNum + 1:pick+preIndex] - output += '\n' + number + ',' + parameter - #print(parameter) - return output[1:] + output = 0 + for i in range(len(chain)): + #print(len(chain)-i - 1) + output += (256**(len(chain)-i - 1))*chain[i] + return output +def getParams(text, object): + global offset, output, stringMode, unClassable, unFieldable, endFlag + fieldNum = intConv(text[offset:offset+4]) + offset += 4 + #print (fieldNum) + while (fieldNum): + if endFlag: + break + #print("field: " + hex(offset-4)) + fieldName = '' + value = 'placeholder' + if fieldNum > 10: + if fieldNum in tables.params: + fieldName = tables.params[fieldNum] + '(' + str(fieldNum) + ')' + if fieldNum in backupFields.bparams: + fieldName = backupFields.bparams[fieldNum] + '(' + str(fieldNum) + ')' + else: + fieldName = "missing_field" + '(' + str(fieldNum) + ')' + unFieldable += 1 + value = parseField(text) + else: + print("weird field detected at " + hex(offset)) + fieldName = text[offset:offset+fieldNum].decode() + offset += fieldNum + value = atoms.Atom(fieldName) + getParams(text, value) + object.add_field(fieldName, value) + fieldNum = intConv(text[offset:offset+4]) + offset += 4 + return + def getClass(text): - global offset, output, stringMode, unClassable, unFieldable + global offset, output, stringMode, unClassable, unFieldable, endFlag + if endFlag: + return '' #print("pos: " + hex(offset)) classNum = intConv(text[offset:offset+4]) offset += 4 @@ -286,25 +275,13 @@ def getClass(text): objName = '' if classNum in tables.objs: objName = tables.objs[classNum] + '(' + str(classNum) + ')' + elif classNum in backupObjects.bobjs: + objName = backupObjects.bobjs[classNum] + '(' + str(classNum) + ')' else: objName = "missing_class" + '(' + str(classNum) + ')' unClassable += 1 object = atoms.Atom(objName) - fieldNum = intConv(text[offset:offset+4]) - offset += 4 - #print (fieldNum) - while (fieldNum): - #print("field: " + hex(offset-4)) - fieldName = '' - if fieldNum in tables.params: - fieldName = tables.params[fieldNum] + '(' + str(fieldNum) + ')' - else: - fieldName = "missing_field" + '(' + str(fieldNum) + ')' - unFieldable += 1 - value = parseField(text) - object.add_field(fieldName, value) - fieldNum = intConv(text[offset:offset+4]) - offset += 4 + getParams(text, object) #print ("broke out of object") return object @@ -395,8 +372,10 @@ def reformat(input): #output = output.replace('data : ', 'data :') #optional return output -def objectify(text): - global offset, output, objList, unClassable, unFieldable, idCount +def bwDecode(text): + global offset, output, objList, unClassable, unFieldable, idCount, endFlag, currentSection + if endFlag: + return '' atoms.resetId() currentSection = 0 output = [] #the array of objects that will be output @@ -405,8 +384,10 @@ def objectify(text): unFieldable = 0 textLength = len(text) offset = 40 - while offset < textLength:#textLength: #should probably use a while loop instead so it doesnt have to iterate through ignored characters + while offset < textLength: #print('p') + if endFlag: + break if currentSection == 0: #getting the header info keyType = intConv(text[offset:offset+4]) offset += 4 @@ -429,6 +410,7 @@ def objectify(text): objList.append(atoms.Atom(name)) elif currentSection == 1: #intermediary section (whitespaces) if (text[offset]) == 0x0a: + #print(output[0]) currentSection = 2 offset+=1 else: @@ -441,32 +423,4 @@ def objectify(text): (unFieldable != 0)*(str(unFieldable) + " unknown fields") + " in this file") else: print("everything probably worked ok. to be honest, i'm not sure.") - return output - #return header + finalOutput - -algos = {0:coords, - 1:settings, - 2:classes, - 3:objectify -} -inputType = [0,0,0,1,1] - -def magic(name, directory): #applies an algorithm to a single file (which algorithm it is is based on the 'mode' variable) - device_data = fs.read_binary(directory + '\\' + name) - output = "" - if (device_data[42] == 0) and inputType[mode]: #not sure how to implement this yet - output = algos[mode](device_data) - finalOutput = '' - for item in output: #encodes all of the objects in the output list - finalOutput += util.json_encode(atoms.serialize(item)) - finalOutput = 'BtWg00010001008d000016a00000000000000000' + reformat(finalOutput) - elif (device_data[42] == '{') and not inputType[mode]: - print("this file is either already converted or isn't an unreadable file") - else: - print("i don't know what kind of file this is") - fs.write_binary(directory + '\output\\converted ' + name, finalOutput.encode("utf-8")) - -#these function calls are for test purposes -#magic('test.bwproject', '.\devices\old devices') -#magic('Amp.bwdevice', '.\devices\old devices') -#print('decoder imported') + return output \ No newline at end of file diff --git a/src/decoder.pyc b/src/decoder.pyc deleted file mode 100644 index dcea436..0000000 Binary files a/src/decoder.pyc and /dev/null differ diff --git a/src/encoder.py b/src/encoder.py new file mode 100644 index 0000000..3153124 --- /dev/null +++ b/src/encoder.py @@ -0,0 +1,51 @@ +import struct +from collections import OrderedDict +from src.lib import atoms + +endFlag = 0 + +def bigOrd(text): + output = 0 + for i in range(len(text)): + #print(len(text)-i - 1) + output += (256**(len(text)-i - 1))*(text[i]) + return output + +def bigChr(chain): + output = '' + for i in range(len(chain)): + #print(len(text)-i - 1) + output += chr(chain[i]) + return output + +def intConv(chain): + output = 0 + for i in range(len(chain)): + #print(len(chain)-i - 1) + output += (256**(len(chain)-i - 1))*chain[i] + return output + +def bwEncode(objList): + global output, endFlag, currentSection + if endFlag: + return '' + atoms.resetId() + currentSection = 0 + output = bytearray(b'') + for object in objList: + if endFlag: + break + if currentSection == 0: + #print (object) + if isinstance(object, atoms.Atom): + output += object.encode() + if currentSection == 1: + output.extend((' '*5000).encode('utf-8')) + output += bytearray.fromhex('0a') + if isinstance(object, atoms.Atom): + output += object.encode() + else: + print('something went wrong, but im gonna keep going. For reference, the oopsie woopsie fucky wucky code is \'section 1 in bwEncode\'') + currentSection+=1 + print("encode complete") + return output \ No newline at end of file diff --git a/src/extractor.py b/src/extractor.py new file mode 100644 index 0000000..1458cc5 --- /dev/null +++ b/src/extractor.py @@ -0,0 +1,238 @@ +import struct +from collections import OrderedDict +from src.lib.luts import tables, backupObjects, backupFields +from src.lib import atoms +import uuid + +stringMode = 0 +endFlag = 0 +def parseField(): + global text, offset, output, stringMode, endFlag, currentSection + if endFlag: + return '' + parseType = text[offset] + offset += 1 + #print(hex(parseType)) + if parseType == 0x01: #8 bit int + offset += 1 + elif parseType == 0x02: #16 bit int + offset += 2 + return 1 + elif parseType == 0x03: #32 bit int + offset += 4 + return 1 + elif parseType == 0x05: #boolean + offset += 1 + elif parseType == 0x06: #float + offset += 4 + elif parseType == 0x07: #double + offset += 8 + elif parseType == 0x08: #string + if stringMode == 0: + stringLength = intConv(text[offset:offset+4]) + offset += 4 + sFormat = 0 #0:utf-8, 1:utf-16 + if (stringLength & 0x80000000): + stringLength &= 0x7fffffff + sFormat = 1 + if (stringLength > 100000): + offset += 4 + return 'too long' + offset += stringLength*(sFormat + 1) + elif stringMode == 1: + while (text[offset]) != 0x00: + if endFlag: + break + offset += 1 + offset += 1 + elif parseType == 0x09: #object + getClass() + elif parseType == 0x0a: #null + return None + elif parseType == 0x0b: #object reference + offset += 4 + return 9 + elif parseType == 0x0d: #structure #not done yet + headerLength = intConv(text[offset:offset+4]) + offset += 4 + if currentSection == 2: + offset += 57 + offset += headerLength + elif headerLength<54: + print("short header at " + hex(offset)) + offset += 54 + stringMode = 1 + getClass() + stringMode = 0 + else: + offset += headerLength + elif parseType == 0x12: #object array + while (intConv(text[offset:offset+4]) != 0x3): + if endFlag: + break + getClass() + offset += 4 + elif parseType == 0x14: #map string + parseType2 = (text[offset]) + offset += 1 + if parseType2 == 0x1: + stringLength = intConv(text[offset:offset+4]) + offset += 4 + offset += stringLength + getClass() + offset+=1 + elif parseType == 0x15: #UUID + offset += 16 + elif parseType == 0x16: #color + offset += 16 + elif parseType == 0x17: #float array + arrayLength = intConv(text[offset:offset+4]) + offset += 4 + offset += arrayLength*4 + elif parseType == 0x19: #string array + arrayLength = intConv(text[offset:offset+4]) + offset += 4 + for i in range(arrayLength): + strLength = intConv(text[offset:offset+4]) + offset += 4 + offset+=strLength + elif parseType == 0x1a: #fuck if i know #not done yet + #print("shit,1a ") + strLength = 0 + while (text[offset] == 0x00): + strLength = intConv(text[offset:offset+4]) + offset += 4 + if strLength == 0x90: + return + offset += strLength + else: + endFlag = 1 + print('unknown type at ' + hex(offset-1) + ', ' + str(parseType)) + return 'end here' + return parseType + +def bigOrd(text): + output = 0 + for i in range(len(text)): + #print(len(text)-i - 1) + output += (256**(len(text)-i - 1))*(text[i]) + return output + +def bigChr(chain): + output = '' + for i in range(len(chain)): + #print(len(text)-i - 1) + output += chr(chain[i]) + return output + +def intConv(chain): + output = 0 + for i in range(len(chain)): + #print(len(chain)-i - 1) + output += (256**(len(chain)-i - 1))*chain[i] + return output + +def addField(fieldName): + global text, fieldList + fieldType = parseField() + if fieldName not in fieldList: + fieldList[fieldName] = fieldType + elif fieldList[fieldName] == None: + fieldList[fieldName] = fieldType + else: + if fieldType and fieldList[fieldName] != fieldType: + print(fieldType) + print(type(fieldList[fieldName])) + print("somethings wrong. oopsie woopsie fucky wucky code is \'different encoder fields\'") + #print(str(fieldList[fieldName]) + ', ' + str(fieldType)) + +def getParams(): + global text, offset, classList, fieldList, unClassable, unFieldable, endFlag + fieldNum = intConv(text[offset:offset+4]) + offset += 4 + output = [] + while (fieldNum): + if endFlag: + break + #print("field: " + hex(offset-4)) + fieldName = '' + value = 'placeholder' + if fieldNum > 10: + #print('f'+hex(fieldNum)) + addField(fieldNum) + else: + print("weird field detected at " + hex(offset)) + fieldName = text[offset:offset+fieldNum].decode() + offset += fieldNum + value = fieldName + classList[fieldName] = getParams() + output.append(fieldNum) + fieldNum = intConv(text[offset:offset+4]) + offset += 4 + return output + +def getClass(): + global text, offset, classList, unClassable, endFlag + if endFlag: + return + classNum = intConv(text[offset:offset+4]) + offset += 4 + if classNum == 0x1: #object references + offset += 4 + else: + #print(classNum) + classList[classNum] = getParams() + return + +def reformat(input): + output = input + i = 0 + length = len(output) + while (i < len(output)): + i+=1 + return output + +def bwExtract(textIn): + global text, offset, classList, fieldList, unClassable, unFieldable, endFlag, currentSection + text = textIn + endFlag = 0 + atoms.resetId() + currentSection = 0 + classList = {} + fieldList = {} + unClassable = 0 + unFieldable = 0 + textLength = len(text) + offset = 40 + list = OrderedDict() + while offset < textLength: + if endFlag: + break + if currentSection == 0: #getting the header info + keyType = intConv(text[offset:offset+4]) + offset += 4 + if keyType == 0x0: #end of array + metaList = list + currentSection = 1 + elif keyType == 0x1: #parameter + keyLength = intConv(text[offset:offset+4]) #length of the string + offset += 4 + key = bigChr(text[offset:offset+keyLength]) + offset += keyLength + addField(key) + elif keyType == 0x4: #object + stringLength = intConv(text[offset:offset+4]) + offset += 4 + name = bigChr(text[offset:offset+stringLength]) + offset += stringLength + elif currentSection == 1: #intermediary section (whitespaces) + if (text[offset]) == 0x0a: + currentSection = 2 + offset+=1 + else: + offset+=1 + elif currentSection == 2: + getClass() + if not endFlag: + print("everything probably worked ok. to be honest, i'm not sure.") + return (classList, fieldList) \ No newline at end of file diff --git a/src/lib/__pycache__/atoms.cpython-36.pyc b/src/lib/__pycache__/atoms.cpython-36.pyc deleted file mode 100644 index 17da73d..0000000 Binary files a/src/lib/__pycache__/atoms.cpython-36.pyc and /dev/null differ diff --git a/src/lib/__pycache__/fs.cpython-36.pyc b/src/lib/__pycache__/fs.cpython-36.pyc deleted file mode 100644 index 1e63898..0000000 Binary files a/src/lib/__pycache__/fs.cpython-36.pyc and /dev/null differ diff --git a/src/lib/__pycache__/util.cpython-36.pyc b/src/lib/__pycache__/util.cpython-36.pyc deleted file mode 100644 index 9643175..0000000 Binary files a/src/lib/__pycache__/util.cpython-36.pyc and /dev/null differ diff --git a/src/lib/atoms.py b/src/lib/atoms.py index aea31a4..7195c1f 100644 --- a/src/lib/atoms.py +++ b/src/lib/atoms.py @@ -2,9 +2,17 @@ from collections import OrderedDict from src.lib import util +from src.lib.luts import typeLists +import uuid +import struct idCount = 0 ## Serializes all device atoms + +def hexPad(data, pad = 8): #probably not good style to include this in the class itself + value = hex(data)[2:] + return (pad-len(value))*'0'+value + def serialize(obj, state = None): if state == None: state = [] @@ -47,12 +55,28 @@ def __init__(self, classNum = 0): def serialize(self): return {'object_ref': self.classNum} + + def encode(self): + output = bytearray(b'') + output += bytearray.fromhex(hexPad(self.classNum,8)) + return output class Color: def __init__(self, rd, gr, bl, al): self.fields = {'type': "color", 'data': [rd, gr, bl, al]} if (al == 1.0): self.fields['data'] = self.fields['data'][:-1] + + def encode(self): + output = bytearray(b'') + count = 0 + for item in self.fields["data"]: + flVal = struct.unpack('= -128: + output += bytearray.fromhex('01') + if value < 0: + #print(hex(0xFF + value + 1)[2:]) + output += bytearray.fromhex(hex(0xFF + value + 1)[2:]) + else: + output += bytearray.fromhex(hexPad(value, 2)) + elif value <= 32767 and value >= -32768: + output += bytearray.fromhex('02') + if value < 0: + #print(value) + #print(hex((value + (1 << 4)) % (1 << 4))) + output += bytearray.fromhex(hex(0xFFFF + value + 1)[2:]) + else: + output += bytearray.fromhex(hexPad(value, 4)) + elif value <= 2147483647 and value >= -2147483648: + output += bytearray.fromhex('03') + if value < 0: + output += bytearray.fromhex(hex(0xFFFFFFFF + value + 1)[2:]) + else: + output += bytearray.fromhex(hexPad(value, 8)) + elif typeLists.fieldList[fieldNum] == 0x05: + output += bytearray.fromhex('05') + output += bytearray.fromhex('01' if value else '00') + elif typeLists.fieldList[fieldNum] == 0x06: + flVal = struct.unpack('