diff --git a/exporters/midi.py b/exporters/midi.py index 74aa546..8fbcd24 100644 --- a/exporters/midi.py +++ b/exporters/midi.py @@ -1,10 +1,15 @@ +from typing import Optional + from midiutil.MidiFile import NoteOff from parsers.patterns import Pattern, Note from midiutil import MIDIFile +from parsers.project import Song + -class PatternToMidiExporter: +class BaseMidiExporter: + """Base class for all midi exporters""" # midi utils uses either ticks of beats (quarter notes) as time # beats are expressed in floats @@ -12,6 +17,19 @@ class PatternToMidiExporter: # so this value is a tracker step duration to use with MIDIUtil MIDI_16TH_NOTE_TIME_VALUE = 0.25 + # def generate_midi(self) -> MIDIFile: + # raise NotImplementedError() + + def write_midi_file(self, path: str): + + midi_file = self.generate_midi() + + with open(path, "wb") as output_file: + midi_file.writeFile(output_file) + + +class PatternToMidiExporter(BaseMidiExporter): + def __init__(self, pattern: Pattern, tempo_bpm=120): self.pattern = pattern @@ -43,38 +61,62 @@ def get_midi_note_value(note: Note): # tracker C4 is 48 return note.value+12 - def generate_midi(self) -> MIDIFile: + def generate_midi(self, midi_file: MIDIFile = None, + instrument_to_midi_track_map: dict = None, + start_time_offset: float = 0) -> MIDIFile: + degrees = [60, 62, 64, 65, 67, 69, 71, 72] # MIDI note number - # tracker tracks are not actual tracks, but voices, - # since every track can use any instrument at even given time and - # every track is monophonic. - # midi tracks typically represent different instruments and are polyphonic - # so we should count number of instruments in pattern and use it as - # number of tracks - instruments = self.get_list_of_instruments() - midi_tracks_count = len(instruments) + if not instrument_to_midi_track_map: + # tracker tracks are not actual tracks, but voices, + # since every track can use any instrument at even given time and + # every track is monophonic. + # midi tracks typically represent different instruments and are polyphonic + # so we should count number of instruments in pattern and use it as + # number of tracks + instruments = self.get_list_of_instruments() + + + # this maps allows us to quickly find midi track for given instrument + # this should be faster than calling instruments.indexOf() + instrument_to_midi_track_map = {} + + for i in range(len(instruments)): + # todo: get actual track names from project file (or are they stored in instrument files?) + # todo: instrument 48 is midi instrument 1 the next 15 are also midi instruments - set their names + # midi_file.addTrackName(track=i, time=0, trackName=f"Instrument {instruments[i]}") + + instrument_to_midi_track_map[instruments[i]] = i + + else: + # instrument_to_midi_track_map is supploed in case we render + # a song. In this case we may have different instruments in different patterns + # and need a mappign for all of them. We also need to create a midi file + # with tracks for all used instruments. + instruments = instrument_to_midi_track_map.keys() - track = 0 channel = 0 - time = 0 # In beats (is it 4:4?) - default_duration = 1 # In beats (is it 4:4?) - tempo = 60 # In BPM default_volume = 127 # 0-127, as per the MIDI standard - # this maps allows us to quickly find midi track for given instrument - # this should be faster than calling instruments.indexOf() - instrument_to_midi_track_map = {} + if not midi_file: + # if we are not supplied with a midi file, + # create a new one (we are supplied with one, e.g. if we render a song + # and we need to append pattern mido to existing file) - midi_file = MIDIFile(midi_tracks_count) - midi_file.addTempo(track=0, time=0, tempo=self.tempo_bpm) + track = 0 - for i in range(len(instruments)): - # todo: get actual track names from project file (or are they stored in instrument files?) - # todo: instrument 48 is midi instrument 1 the next 15 are also midi instruments - set their names - midi_file.addTrackName(track=i, time=0, trackName=f"Instrument {instruments[i]}") + time = 0 # In beats (is it 4:4?) + default_duration = 1 # In beats (is it 4:4?) + tempo = 60 # In BPM - instrument_to_midi_track_map[instruments[i]] = i + midi_tracks_count = len(instruments) + midi_file = MIDIFile(midi_tracks_count) + midi_file.addTempo(track=0, time=0, tempo=self.tempo_bpm) + + for i in range(len(instruments)): + # todo: get actual track names from project file (or are they stored in instrument files?) + # todo: instrument 48 is midi instrument 1 the next 15 are also midi instruments - set their names + midi_file.addTrackName(track=i, time=0, trackName=f"Instrument {instruments[i]}") for track in self.pattern.tracks: @@ -119,10 +161,7 @@ def generate_midi(self) -> MIDIFile: duration = (note_end_position - step_number) * PatternToMidiExporter.MIDI_16TH_NOTE_TIME_VALUE - - # TODO: add support for chord fx - # TODO: add support for arp fx - + # add actual notes to midi file data if step.get_arp(): # arpeggio arp = step.get_arp() @@ -169,7 +208,7 @@ def generate_midi(self) -> MIDIFile: midi_file.addNote(track=instrument_to_midi_track_map[step.instrument_number], channel=channel, pitch=PatternToMidiExporter.get_midi_note_value(note), - time=arp_note_start_time, + time=start_time_offset + arp_note_start_time, duration=arp_note_duration, # TODO: write velocity fx value if set (needs to be converted to 0...127!!!) volume=default_volume, @@ -187,7 +226,7 @@ def generate_midi(self) -> MIDIFile: midi_file.addNote(track=instrument_to_midi_track_map[step.instrument_number], channel=channel, pitch=PatternToMidiExporter.get_midi_note_value(note), - time=step_number * PatternToMidiExporter.MIDI_16TH_NOTE_TIME_VALUE, + time=start_time_offset + step_number * PatternToMidiExporter.MIDI_16TH_NOTE_TIME_VALUE, duration=duration, # TODO: write velocity fx value if set (needs to be converted to 0...127!!!) volume=default_volume, @@ -198,7 +237,7 @@ def generate_midi(self) -> MIDIFile: midi_file.addNote(track=instrument_to_midi_track_map[step.instrument_number], channel=channel, pitch=PatternToMidiExporter.get_midi_note_value(step.note), - time=step_number*PatternToMidiExporter.MIDI_16TH_NOTE_TIME_VALUE, + time=start_time_offset + step_number*PatternToMidiExporter.MIDI_16TH_NOTE_TIME_VALUE, duration=duration, # TODO: write velocity fx value if set (needs to be converted to 0...127!!!) volume=default_volume, @@ -206,12 +245,78 @@ def generate_midi(self) -> MIDIFile: return midi_file - def write_midi_file(self, path: str): - midi_file = self.generate_midi() +class SongToMidiExporter(BaseMidiExporter): - with open(path, "wb") as output_file: - midi_file.writeFile(output_file) + def __init__(self, song: Song): + self.song = song + + def get_list_of_instruments(self): + """ + Gets list of all actually used instruments + across all patterns of the song. + Used to create midi file with proper instrument tracks. + :return: + """ + instruments = set() + # iterate over unique patterns only + for pattern in self.song.pattern_mapping.values(): + instruments.update(PatternToMidiExporter(pattern=pattern).get_list_of_instruments()) + + return sorted(instruments) + + def generate_midi(self) -> MIDIFile: + # raise NotImplementedError() + + # tracker tracks are not actual tracks, but voices, + # since every track can use any instrument at even given time and + # every track is monophonic. + # midi tracks typically represent different instruments and are polyphonic + # so we should count number of instruments in pattern and use it as + # number of tracks + instruments = self.get_list_of_instruments() + midi_tracks_count = len(instruments) + + # this maps allows us to quickly find midi track for given instrument + # this should be faster than calling instruments.indexOf() + instrument_to_midi_track_map = {} + midi_file = MIDIFile(midi_tracks_count) + #FIXME: write bpm to song to get it from there + midi_file.addTempo(track=0, time=0, tempo=self.song.bpm) + for i in range(len(instruments)): + # todo: get actual track names from project file (or are they stored in instrument files?) + # todo: instrument 48 is midi instrument 1 the next 15 are also midi instruments - set their names + midi_file.addTrackName(track=i, time=0, trackName=f"Instrument {instruments[i]}") + instrument_to_midi_track_map[instruments[i]] = i + + print(f"instruments: {instruments}") + print(f"instrument_to_midi_track_map: {instrument_to_midi_track_map}") + + previous_pattern: Optional[Pattern] = None + + j = 0 + start_time_offset = 0 + print(self.song.pattern_chain) + for pattern in self.song.get_song_as_patterns(): #fixme: temporary slice for debug + j+=1 + print(f"Rendering song slot {j}") + exporter = PatternToMidiExporter(pattern=pattern) + + if previous_pattern: + # every next pattern should write midi data + # after the previous pattern ended, + # so we have to add time offset for every pattern + start_time_offset += previous_pattern.tracks[0].length * SongToMidiExporter.MIDI_16TH_NOTE_TIME_VALUE + + # todo: add arguments and handle them + # todo: no need to do value declaration here really, we already pass it by reference + midi_file = exporter.generate_midi(midi_file=midi_file, + instrument_to_midi_track_map=instrument_to_midi_track_map, + start_time_offset=start_time_offset) + + previous_pattern = pattern + + return midi_file diff --git a/main.py b/main.py index b2f9a63..c3dafe3 100644 --- a/main.py +++ b/main.py @@ -1,18 +1,30 @@ if __name__ == '__main__': - from parsers import patterns - - # todo: remove this and implement tests - p = patterns.PatternParser( - # NOTE: this file was created with firmware 1.3.1 or older version - filename="./reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns/pattern_06.mtp") - parsed_pattern = p.parse() - # print(parsed_pattern.render_as_table()) - from exporters import midi - - midi_exporter = midi.PatternToMidiExporter(pattern=parsed_pattern) - print(midi_exporter.generate_midi()) - midi_exporter.write_midi_file("./test_midi_file.mid") + from parsers import patterns, project + + # # todo: remove this and implement tests + # p = patterns.PatternParser( + # # NOTE: this file was created with firmware 1.3.1 or older version + # filename="./reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns/pattern_06.mtp") + # parsed_pattern = p.parse() + # # print(parsed_pattern.render_as_table()) + # from exporters import midi + # + # midi_exporter = midi.PatternToMidiExporter(pattern=parsed_pattern) + # print(midi_exporter.generate_midi()) + # midi_exporter.write_midi_file("./test_midi_file.mid") + + + project = project.ProjectParser( + filename_or_folder="./reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/" + ) + + parsed_project = project.parse() + print("Finished parsing project.") + print(f"BPM: {parsed_project.song.bpm}") + print(f"pattern mapping: {parsed_project.song.pattern_mapping}") + print(f"pattern chain: {parsed_project.song.pattern_chain}") + print(f"song as patterns: {parsed_project.song.get_song_as_patterns()}") diff --git a/parsers/project.py b/parsers/project.py new file mode 100644 index 0000000..83dd5f7 --- /dev/null +++ b/parsers/project.py @@ -0,0 +1,206 @@ +# polyend tracker project file + +__author__ = "Alexey 'DataGreed' Strelkov" + +import os +import struct +from typing import List, Dict + +from parsers.patterns import Pattern + + +class Song: + """ + Represents a song, a sequence of patterns + """ + + """Maximum length of a song in patterns.""" + MAXIMUM_SLOTS_PER_SONG = 255 # from docs + + def __init__(self, pattern_chain: List[int], pattern_mapping: Dict[int, Pattern], + bpm:float): + """ + + :param pattern_chain: a list of ints representing the order of patterns in the song. + ints a 1-based. So pattern 1 is represented by 1. + :param pattern_mapping: a dict with all of the unique patterns in the song. + Keys are integer number of patterns, values are actual patterns. + """ + self.bpm = bpm + if len(pattern_chain)>self.MAXIMUM_SLOTS_PER_SONG: + raise ValueError(f"Maximum song length is {self.MAXIMUM_SLOTS_PER_SONG}, " + f"but {len(pattern_chain)} pattern long song received") + self.pattern_mapping = pattern_mapping + self.pattern_chain = pattern_chain + + # integrity check + for i in set(pattern_chain): + if i not in pattern_mapping.keys(): + raise ValueError(f"Song initialization: pattern_chain has value " + f"{i}, but there is no such pattern in pattern_mapping. " + f"Context: pattern_chain: {pattern_chain}; " + f"pattern_mapping: {pattern_mapping}") + + def get_song_as_patterns(self) -> List[Pattern]: + """Returns song as a list of patterns ordered. Play them in + the returned order to get the song""" + return [self.pattern_mapping[x] for x in self.pattern_chain] + + +class Project: + """ + Represents a tracker project + """ + + def __init__(self, name: str, song: Song): + """ + :param name: name of the project + :param bpm: project tempo in beats per minutes. Tracker supports fractional tempo + :param song: song (sequence of patterns to play) + """ + self.name = name + self.song = song + + OFFSET_END = 0x624 #1572 bytes total files length + OFFSET_START = 0 + + PATTERN_CHAIN_OFFSET = 0x10 + PATTERN_CHAIN_END = 0x10f # length is 255 bytes + + BPM_OFFSET_START = 0x1c0 + BPM_BYTES_LENGTH = 4 # it's a 32bit float. The 40-800 and limit and 0.1 precision are artificial + + @staticmethod + def from_bytes(data: bytes, patterns_bytes=Dict[int,bytes]) -> "Project": + """ + Constructs a project object from bytes extracted from project file. + :param data: + :param patterns_bytes: a list of bytes for each of projects + patterns extracted from pattern files. Note: expects full file + byte representation without offsets. + :return: + """ + expected_length = Project.OFFSET_END - Project.OFFSET_START # 6152 or 769*8 just for sanity check + + if len(data) != expected_length: + raise ValueError(f"Expected project data {expected_length} bytes long, got {len(data)} instead") + + patterns_mapping = {} + + # parse patterns from received bytes for each pattern file + # and save them in a dict tha maps pattern number to Pattern object + # so it can be used later to construct a song + for key, value in patterns_bytes.items(): + patterns_mapping[key] = Pattern.from_bytes(value[Pattern.OFFSET_START:Pattern.OFFSET_END]) + + bpm = Project.bpm_from_bytes(data[Project.BPM_OFFSET_START:Project.BPM_OFFSET_START+Project.BPM_BYTES_LENGTH]) + + pattern_chain = Project.pattern_chain_from_bytes(data[Project.PATTERN_CHAIN_OFFSET:Project.PATTERN_CHAIN_END]) + + # todo: extract from project files footer + # tracker project files actually store file names inside of them, but + # the problem is that it seems to be buggy, since a lot of times i see + # part of the previous project names in new projects with shorter names. + # not sure if it's actually possible to unpack a real, not mangled name + # from a project file. + name = "MyProject" + + return Project(name=name, + song=Song(pattern_chain=pattern_chain, + pattern_mapping=patterns_mapping, + bpm=bpm), + ) + + @staticmethod + def pattern_chain_from_bytes(data: bytes) -> List[int]: + """extracts chain of patterns for song from project file bytes""" + + # print("EXTRACTING pattern chain from sequence of bytes below:") + # print(data) + # print(f"data length {len(data)}") + + result = [] + + for byte in data: + # each byte is just a pattern number + # print(byte) + if byte: + result.append(byte) + else: + # break when we encounter zero. It stand for unoccupied slot. + # there could be no unoccupied slots in a sond (I guess?) + # todo: check that it's correct + break + + return result + + @staticmethod + def bpm_from_bytes(data: bytes) -> List[int]: + + if len(data) != 4: + raise ValueError(f"Length of data for BPM should be 4 bytes, but received {len(bytes)}: {bytes}") + # unpacks 4 bytes to a float + return round(struct.unpack('f', data)[0], 1) + + + +class ProjectParser: + """Parses tracker *.mt files, scans directory for patterns + so a song can be exported to MIDI""" + + PATTERNS_FOLDER_NAME = "patterns" + DEFAULT_PROJECT_FILENAME = "project.mt" + PATTERN_FILE_NAME_TEMPLATE = "pattern_{}.mtp" # todo: format: add leading zero automatically + + MAXIMUM_PATTERNS_PER_PROJECT = 255 # from polyend docs + + def __init__(self, filename_or_folder: str): + """ + :param filename_or_folder: project (*.mt) filename or folder with project file. + Note that pattern files are required to be in a "patterns" subfolder within + the same folder for everything to work properly. + """ + if filename_or_folder.endswith(".mt"): + self.filepath = filename_or_folder + # folder must always end with a folder separator + self.folder = os.sep.join(filename_or_folder.split(os.sep)[:-1]) + os.sep + else: + self.folder = filename_or_folder + if not self.folder.endswith(os.sep): + # folder must always end with a folder separator + self.folder += os.sep + self.filepath = self.folder+self.DEFAULT_PROJECT_FILENAME + + self.patterns_folder = self.folder + self.PATTERNS_FOLDER_NAME + os.sep + + def parse(self) -> Project: + + project_file_bytes = None + pattern_file_bytes_dict: Dict[int, bytes] = {} + + with open(self.filepath, "rb") as f: + project_file_bytes = f.read() # f.read()[Project.OFFSET_START:Project.OFFSET_END] + + # find all project pattern files and add their bytes to the parser too + for i in range(1, self.MAXIMUM_PATTERNS_PER_PROJECT+1): + pattern_file_path = self.patterns_folder+self.PATTERN_FILE_NAME_TEMPLATE + + # FIXME: patterns names goe as pattern_01.mtp, but manual says there can be + # 255 patterns, how pattern 100 file will be named if + # there is only 1 leading zero in one digit projects? + + pattern_number_string = str(i) + if len(pattern_number_string) < 2: + pattern_number_string = "0" + pattern_number_string + + pattern_file_path = pattern_file_path.replace("{}", pattern_number_string) + + try: + with open(pattern_file_path, "rb") as f: + # pattern + pattern_file_bytes_dict[i] = f.read() # reads the whole file + except FileNotFoundError as e: + # it's okay. Not all patterns have to exist. + pass + + return Project.from_bytes(project_file_bytes, pattern_file_bytes_dict) diff --git a/polytracker2midi.py b/polytracker2midi.py index ab06527..cac7502 100644 --- a/polytracker2midi.py +++ b/polytracker2midi.py @@ -2,7 +2,7 @@ import sys from sys import argv -from parsers import patterns +from parsers import patterns, project from exporters import midi @@ -26,7 +26,16 @@ def main(): # generate output filename from an input one by changing extension # if provided - output_filename = ".".join(input_filename.split(".")[:-1]) + ".mid" + if os.path.isdir(input_filename): + + output_filename = input_filename + if not output_filename.endswith(os.sep): + output_filename += os.sep + + output_filename += "project.mid" + + else: + output_filename = ".".join(input_filename.split(".")[:-1]) + ".mid" try: # try to get output filename from second command line argument @@ -39,22 +48,62 @@ def main(): # not provided - use default one pass - if not os.path.isfile(input_filename): + if not (os.path.isfile(input_filename) or os.path.isdir(input_filename)): print(f"File {input_filename} does not exist") sys.exit(1) if os.path.isfile(output_filename): print(f"File {output_filename} already exists - will overwrite") - p = patterns.PatternParser(filename=input_filename) - parsed_pattern = p.parse() + if input_filename.endswith(".mtp"): + print("Trying to parse a pattern file...") + p = patterns.PatternParser(filename=input_filename) + parsed_pattern = p.parse() + + # print(parsed_pattern.render_as_table()) + + midi_exporter = midi.PatternToMidiExporter(pattern=parsed_pattern) + midi_exporter.write_midi_file(output_filename) + + print(f"Exported pattern midi to {os.path.abspath(output_filename)}") + + else: + print("Trying to parse a project...") + # try to parse as project + p = project.ProjectParser(filename_or_folder=input_filename) + parsed_project = p.parse() + + # print(parsed_pattern.render_as_table()) + + midi_exporter = midi.SongToMidiExporter(song=parsed_project.song) + midi_exporter.write_midi_file(output_filename) + + print(f"Exported project midi to {os.path.abspath(output_filename)}") + + # todo: export individual patterns, too + + print("Trying to export patterns...") + + for number, pattern in parsed_project.song.pattern_mapping.items(): + midi_exporter = midi.PatternToMidiExporter(pattern=pattern, tempo_bpm=int(parsed_project.song.bpm)) + + number_string = str(number) + if len(number_string) < 2: + number_string = "0" + number_string + + # create directory for patterns + # in the same folder we export project to + out_folder = os.sep.join(output_filename.split(os.sep)[:-1]) + os.sep - # print(parsed_pattern.render_as_table()) + try: + os.mkdir(out_folder + "patterns_midi/") + except FileExistsError: + pass - midi_exporter = midi.PatternToMidiExporter(pattern=parsed_pattern) - midi_exporter.write_midi_file(output_filename) + pattern_output_filename = out_folder + "patterns_midi/" + f"pattern_{number_string}.mid" + midi_exporter.write_midi_file(pattern_output_filename) + print(f"Exported pattern midi to {os.path.abspath(pattern_output_filename)}") - print(f"Exported midi to {os.path.abspath(output_filename)}") if __name__ == '__main__': diff --git a/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns/pattern_26.mid b/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns/pattern_26.mid new file mode 100644 index 0000000..6152167 Binary files /dev/null and b/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns/pattern_26.mid differ diff --git a/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns/pattern_42.mid b/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns/pattern_42.mid new file mode 100644 index 0000000..259b667 Binary files /dev/null and b/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns/pattern_42.mid differ diff --git a/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns/pattern_42.txt b/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns/pattern_42.txt new file mode 100644 index 0000000..a82f1d6 --- /dev/null +++ b/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns/pattern_42.txt @@ -0,0 +1,33 @@ + Track 1 | Track 2 | Track 3 | Track 4 | Track 5 | Track 6 | Track 7 | Track 8 +--- -- -- --- -- --- | OFF 49 -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- V 42 -- --- | --- -- -- --- -- --- +--- -- -- --- -- --- | A3 49 -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | C5 27 V 46 -- --- | --- -- -- --- -- --- +--- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | C5 27 V 47 -- --- | --- -- -- --- -- --- +--- -- -- --- -- --- | OFF -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | C5 27 V 43 -- --- | --- -- -- --- -- --- +--- -- -- --- -- --- | A3 49 -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | C5 27 V 45 -- --- | --- -- -- --- -- --- +--- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | C5 27 V 45 -- --- | --- -- -- --- -- --- +--- -- -- --- -- --- | OFF -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | C5 27 V 46 -- --- | --- -- -- --- -- --- +--- -- -- --- -- --- | A3 49 -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | C5 27 V 44 -- --- | --- -- -- --- -- --- +--- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- V 45 -- --- | --- -- -- --- -- --- +--- -- -- --- -- --- | OFF -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | C5 27 V 46 -- --- | --- -- -- --- -- --- +--- -- -- --- -- --- | A3 49 -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | C5 27 V 48 -- --- | --- -- -- --- -- --- +--- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | C5 27 V 48 -- --- | --- -- -- --- -- --- +--- -- -- --- -- --- | OFF -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | C5 27 V 44 -- --- | --- -- -- --- -- --- +--- -- -- --- -- --- | A3 49 -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | C5 27 V 44 -- --- | --- -- -- --- -- --- +--- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | C5 27 V 46 -- --- | --- -- -- --- -- --- +--- -- -- --- -- --- | OFF -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | C5 27 V 43 -- --- | --- -- -- --- -- --- +--- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- V 47 -- --- | --- -- -- --- -- --- +--- -- -- --- -- --- | A3 49 -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | C5 27 V 47 -- --- | --- -- -- --- -- --- +--- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | C5 27 V 48 -- --- | --- -- -- --- -- --- +--- -- -- --- -- --- | OFF -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | C5 27 V 46 -- --- | --- -- -- --- -- --- +--- -- -- --- -- --- | A3 49 -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | C5 27 V 44 -- --- | --- -- -- --- -- --- +--- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | C5 27 V 46 -- --- | --- -- -- --- -- --- +--- -- -- --- -- --- | OFF -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | C5 27 V 45 -- --- | --- -- -- --- -- --- +--- -- -- --- -- --- | C4 49 -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | C5 27 V 43 -- --- | --- -- -- --- -- --- +--- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- V 48 -- --- | --- -- -- --- -- --- +--- -- -- --- -- --- | OFF -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | C5 27 V 47 -- --- | --- -- -- --- -- --- +--- -- -- --- -- --- | A#3 49 -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | C5 27 V 46 -- --- | --- -- -- --- -- --- +--- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | C5 27 V 48 -- --- | --- -- -- --- -- --- +--- -- -- --- -- --- | OFF 49 -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | C5 27 V 44 -- --- | --- -- -- --- -- --- +--- -- -- --- -- --- | D4 49 -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | C5 27 V 43 -- --- | --- -- -- --- -- --- +--- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | C5 27 V 44 -- --- | --- -- -- --- -- --- +--- -- -- --- -- --- | --- 49 -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | --- -- -- --- -- --- | C5 27 V 44 -- --- | --- -- -- --- -- --- \ No newline at end of file diff --git a/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_01.mid b/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_01.mid new file mode 100644 index 0000000..9f057e7 Binary files /dev/null and b/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_01.mid differ diff --git a/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_02.mid b/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_02.mid new file mode 100644 index 0000000..c32a9ae Binary files /dev/null and b/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_02.mid differ diff --git a/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_03.mid b/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_03.mid new file mode 100644 index 0000000..5a92997 Binary files /dev/null and b/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_03.mid differ diff --git a/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_04.mid b/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_04.mid new file mode 100644 index 0000000..1466fcf Binary files /dev/null and b/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_04.mid differ diff --git a/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_05.mid b/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_05.mid new file mode 100644 index 0000000..13b92da Binary files /dev/null and b/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_05.mid differ diff --git a/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_06.mid b/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_06.mid new file mode 100644 index 0000000..c99cffc Binary files /dev/null and b/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_06.mid differ diff --git a/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_07.mid b/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_07.mid new file mode 100644 index 0000000..c99cffc Binary files /dev/null and b/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_07.mid differ diff --git a/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_09.mid b/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_09.mid new file mode 100644 index 0000000..2b6541b Binary files /dev/null and b/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_09.mid differ diff --git a/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_26.mid b/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_26.mid new file mode 100644 index 0000000..6152167 Binary files /dev/null and b/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_26.mid differ diff --git a/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_27.mid b/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_27.mid new file mode 100644 index 0000000..2d2f976 Binary files /dev/null and b/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_27.mid differ diff --git a/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_28.mid b/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_28.mid new file mode 100644 index 0000000..949e888 Binary files /dev/null and b/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_28.mid differ diff --git a/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_29.mid b/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_29.mid new file mode 100644 index 0000000..0204029 Binary files /dev/null and b/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_29.mid differ diff --git a/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_30.mid b/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_30.mid new file mode 100644 index 0000000..2b6541b Binary files /dev/null and b/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_30.mid differ diff --git a/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_31.mid b/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_31.mid new file mode 100644 index 0000000..451bf02 Binary files /dev/null and b/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_31.mid differ diff --git a/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_32.mid b/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_32.mid new file mode 100644 index 0000000..2bb0837 Binary files /dev/null and b/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_32.mid differ diff --git a/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_42.mid b/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_42.mid new file mode 100644 index 0000000..259b667 Binary files /dev/null and b/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_42.mid differ diff --git a/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_43.mid b/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_43.mid new file mode 100644 index 0000000..d81f8ca Binary files /dev/null and b/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_43.mid differ diff --git a/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_44.mid b/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_44.mid new file mode 100644 index 0000000..c99cffc Binary files /dev/null and b/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_44.mid differ diff --git a/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_45.mid b/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_45.mid new file mode 100644 index 0000000..c4be7e5 Binary files /dev/null and b/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_45.mid differ diff --git a/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_51.mid b/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_51.mid new file mode 100644 index 0000000..655a70a Binary files /dev/null and b/reverse-engineering/session 1/project files/datagreed - rebel path tribute 2/patterns_midi/pattern_51.mid differ diff --git a/reverse-engineering/session 2/project files only for analysis/empty 120 bpm.mt b/reverse-engineering/session 2/project files only for analysis/empty 120 bpm.mt new file mode 100755 index 0000000..920878c Binary files /dev/null and b/reverse-engineering/session 2/project files only for analysis/empty 120 bpm.mt differ diff --git a/reverse-engineering/session 2/project files only for analysis/empty 120.1 bpm.mt b/reverse-engineering/session 2/project files only for analysis/empty 120.1 bpm.mt new file mode 100755 index 0000000..19dce62 Binary files /dev/null and b/reverse-engineering/session 2/project files only for analysis/empty 120.1 bpm.mt differ diff --git a/reverse-engineering/session 2/project files only for analysis/empty 120.5 bpm.mt b/reverse-engineering/session 2/project files only for analysis/empty 120.5 bpm.mt new file mode 100755 index 0000000..3e7f73e Binary files /dev/null and b/reverse-engineering/session 2/project files only for analysis/empty 120.5 bpm.mt differ diff --git a/reverse-engineering/session 2/project files only for analysis/empty 172 bpm.mt b/reverse-engineering/session 2/project files only for analysis/empty 172 bpm.mt new file mode 100755 index 0000000..b867eda Binary files /dev/null and b/reverse-engineering/session 2/project files only for analysis/empty 172 bpm.mt differ diff --git a/reverse-engineering/session 2/project files only for analysis/empty130 bpm.mt b/reverse-engineering/session 2/project files only for analysis/empty130 bpm.mt new file mode 100755 index 0000000..f3b24ac Binary files /dev/null and b/reverse-engineering/session 2/project files only for analysis/empty130 bpm.mt differ diff --git a/reverse-engineering/session 2/project files only for analysis/empty131 bpm.mt b/reverse-engineering/session 2/project files only for analysis/empty131 bpm.mt new file mode 100755 index 0000000..a50335b Binary files /dev/null and b/reverse-engineering/session 2/project files only for analysis/empty131 bpm.mt differ diff --git a/reverse-engineering/session 2/project files only for analysis/not sure project.mt b/reverse-engineering/session 2/project files only for analysis/not sure project.mt new file mode 100755 index 0000000..379c5ef Binary files /dev/null and b/reverse-engineering/session 2/project files only for analysis/not sure project.mt differ diff --git a/reverse-engineering/session 2/project files only for analysis/patterns 1-1 130 bpm.mt b/reverse-engineering/session 2/project files only for analysis/patterns 1-1 130 bpm.mt new file mode 100755 index 0000000..65ac2ff Binary files /dev/null and b/reverse-engineering/session 2/project files only for analysis/patterns 1-1 130 bpm.mt differ diff --git a/reverse-engineering/session 2/project files only for analysis/patterns 1-1-1 130 bpm.mt b/reverse-engineering/session 2/project files only for analysis/patterns 1-1-1 130 bpm.mt new file mode 100755 index 0000000..379c5ef Binary files /dev/null and b/reverse-engineering/session 2/project files only for analysis/patterns 1-1-1 130 bpm.mt differ diff --git a/reverse-engineering/session 2/project files only for analysis/patterns 1-1-2 130 bpm.mt b/reverse-engineering/session 2/project files only for analysis/patterns 1-1-2 130 bpm.mt new file mode 100755 index 0000000..f0a472a Binary files /dev/null and b/reverse-engineering/session 2/project files only for analysis/patterns 1-1-2 130 bpm.mt differ diff --git a/reverse-engineering/session 2/project files only for analysis/seems to be rebel path.mt b/reverse-engineering/session 2/project files only for analysis/seems to be rebel path.mt new file mode 100755 index 0000000..8f2d9a8 Binary files /dev/null and b/reverse-engineering/session 2/project files only for analysis/seems to be rebel path.mt differ diff --git a/reverse-engineering/session 2/session2-notes.md b/reverse-engineering/session 2/session2-notes.md new file mode 100644 index 0000000..1d37243 --- /dev/null +++ b/reverse-engineering/session 2/session2-notes.md @@ -0,0 +1,45 @@ +# Session 2 reverse engineering notes + +Object: Polyend Tracker project `*.mt` files + +## BPM reverse engineering + +From docs: +goes from 40 to 800BPM + +From files I have it seems that bpm fraction part and integer part are stored in different offsets. + +integer part: +- 0x1c2 - 2 bytes long (could be more, need to check at 800 bpm) + +examples: +- 120 bpm - F0 42 +- 130 bpm - 02 43 +- 131 bpm - 03 43 +- 172 bpm - 2C 43 (am I mistaken? is it actually 171 bpm? would make more sense probably) +- 90? bpm - B4 42 (not sure if it was actually 90 bpm, gotta check) + +no idea how to convert bpm values yet. I may have incorrectly named files according to bpms. +Have to double check and resave them. + +project files conainns file name at the end and track names - hex viewer see tham as ascii. + +Seems like some of my test files are saved with incorrectly according to their contents. + +It seems that the song pattern sequence starts at 0x10 and each bytes represents a pattern number. + +E.g. 01 01 02 stands for 1,1,2 patterns + +so the offset seems to be 0x10 with length of 256 bytes + +# upd +Okay, I've re-saved the files again properly with names corresponding to bpms. +I see that bpms with fractions take 4 bytes. Seems like they just use a float. + +Checked 40.1 bpm: + +> round(struct.unpack('f', bytes([0x66,0x66,0x20,0x42]))[0], 1) +> 40.1 +> +Yup, that is correct. offset 0x1c and length is 4 bytes. + diff --git a/reverse-engineering/session 3/bpm/131.mt b/reverse-engineering/session 3/bpm/131.mt new file mode 100755 index 0000000..5fa2577 Binary files /dev/null and b/reverse-engineering/session 3/bpm/131.mt differ diff --git a/reverse-engineering/session 3/bpm/40.0.mt b/reverse-engineering/session 3/bpm/40.0.mt new file mode 100755 index 0000000..0599f8a Binary files /dev/null and b/reverse-engineering/session 3/bpm/40.0.mt differ diff --git a/reverse-engineering/session 3/bpm/40.1.mt b/reverse-engineering/session 3/bpm/40.1.mt new file mode 100755 index 0000000..9591a47 Binary files /dev/null and b/reverse-engineering/session 3/bpm/40.1.mt differ diff --git a/reverse-engineering/session 3/bpm/40.2.mt b/reverse-engineering/session 3/bpm/40.2.mt new file mode 100755 index 0000000..185cbfb Binary files /dev/null and b/reverse-engineering/session 3/bpm/40.2.mt differ diff --git a/reverse-engineering/session 3/bpm/40.3.mt b/reverse-engineering/session 3/bpm/40.3.mt new file mode 100755 index 0000000..a589109 Binary files /dev/null and b/reverse-engineering/session 3/bpm/40.3.mt differ diff --git a/reverse-engineering/session 3/bpm/40.4.mt b/reverse-engineering/session 3/bpm/40.4.mt new file mode 100755 index 0000000..3b4bc94 Binary files /dev/null and b/reverse-engineering/session 3/bpm/40.4.mt differ diff --git a/reverse-engineering/session 3/bpm/40.5.mt b/reverse-engineering/session 3/bpm/40.5.mt new file mode 100755 index 0000000..7b56951 Binary files /dev/null and b/reverse-engineering/session 3/bpm/40.5.mt differ diff --git a/reverse-engineering/session 3/bpm/40.6.mt b/reverse-engineering/session 3/bpm/40.6.mt new file mode 100755 index 0000000..5297aab Binary files /dev/null and b/reverse-engineering/session 3/bpm/40.6.mt differ diff --git a/reverse-engineering/session 3/bpm/40.7.mt b/reverse-engineering/session 3/bpm/40.7.mt new file mode 100755 index 0000000..81f3739 Binary files /dev/null and b/reverse-engineering/session 3/bpm/40.7.mt differ diff --git a/reverse-engineering/session 3/bpm/40.8.mt b/reverse-engineering/session 3/bpm/40.8.mt new file mode 100755 index 0000000..cee51e0 Binary files /dev/null and b/reverse-engineering/session 3/bpm/40.8.mt differ diff --git a/reverse-engineering/session 3/bpm/40.9.mt b/reverse-engineering/session 3/bpm/40.9.mt new file mode 100755 index 0000000..360c991 Binary files /dev/null and b/reverse-engineering/session 3/bpm/40.9.mt differ diff --git a/reverse-engineering/session 3/bpm/41.0.mt b/reverse-engineering/session 3/bpm/41.0.mt new file mode 100755 index 0000000..9e5e8cf Binary files /dev/null and b/reverse-engineering/session 3/bpm/41.0.mt differ diff --git a/reverse-engineering/session 3/bpm/41.1.mt b/reverse-engineering/session 3/bpm/41.1.mt new file mode 100755 index 0000000..c0775c1 Binary files /dev/null and b/reverse-engineering/session 3/bpm/41.1.mt differ diff --git a/reverse-engineering/session 3/bpm/41.2.mt b/reverse-engineering/session 3/bpm/41.2.mt new file mode 100755 index 0000000..c03a461 Binary files /dev/null and b/reverse-engineering/session 3/bpm/41.2.mt differ diff --git a/reverse-engineering/session 3/bpm/41.3.mt b/reverse-engineering/session 3/bpm/41.3.mt new file mode 100755 index 0000000..3b1a0b0 Binary files /dev/null and b/reverse-engineering/session 3/bpm/41.3.mt differ diff --git a/reverse-engineering/session 3/bpm/42.mt b/reverse-engineering/session 3/bpm/42.mt new file mode 100755 index 0000000..db0ea23 Binary files /dev/null and b/reverse-engineering/session 3/bpm/42.mt differ diff --git a/reverse-engineering/session 3/bpm/50.mt b/reverse-engineering/session 3/bpm/50.mt new file mode 100755 index 0000000..81709da Binary files /dev/null and b/reverse-engineering/session 3/bpm/50.mt differ diff --git a/reverse-engineering/session 3/bpm/51.mt b/reverse-engineering/session 3/bpm/51.mt new file mode 100755 index 0000000..2950013 Binary files /dev/null and b/reverse-engineering/session 3/bpm/51.mt differ diff --git a/reverse-engineering/session 3/bpm/52.mt b/reverse-engineering/session 3/bpm/52.mt new file mode 100755 index 0000000..7db195e Binary files /dev/null and b/reverse-engineering/session 3/bpm/52.mt differ diff --git a/reverse-engineering/session 3/bpm/799.mt b/reverse-engineering/session 3/bpm/799.mt new file mode 100755 index 0000000..13a78e3 Binary files /dev/null and b/reverse-engineering/session 3/bpm/799.mt differ diff --git a/reverse-engineering/session 3/bpm/800.mt b/reverse-engineering/session 3/bpm/800.mt new file mode 100755 index 0000000..40af533 Binary files /dev/null and b/reverse-engineering/session 3/bpm/800.mt differ