#!/usr/bin/python # -*- coding: utf-8 -*- """ A cross-platform Xbox trainer manager Author: nodots License: GNU GPL v3 """ import sys, binascii, os, struct, tty, stat, math from os import path #wrapper for compatibility try: # for Python2 import Tkinter as tk import ScrolledText as tkst except ImportError: # for Python3 import tkinter as tk import tkinter.scrolledtext as tkst programname = "Python Trainer Manager" version = "0.8" status = "beta" filetypes = [".etm",".xbtf"] printf = sys.stdout.write fbg = '#cfcfcf' Options = 0x0E ID = 0x12 trainerpath = path.join(path.dirname(path.abspath(__file__)), "trainers") # ### NON-CLASSED FUNCTIONALITY ### ### ### ### ### ### ### ### ### ### ### def Ctyperead(ETMdata,ptr): text = "" byte = ETMdata[ptr] if byte == "\0": byte = "" while byte: if byte: text = text + str(byte) ptr += 1 byte = ETMdata[ptr] if byte == "\0": byte = "" try: while text[-1] == " ": text = text[0:len(text)-1] except: pass if text == " ": text = "" return(text) def xbtfread(xbtffile): X = open(xbtffile, 'rb') X.seek(0x27) al = ord(X.read(1)) X.seek(0x2f) al = al + ord(X.read(1)) X.seek(0x37) al = al + ord(X.read(1)) ax = al * 0x0FFFFFF ax = int(format(ax, 'X')[-8:], 16) X.seek(0x00) bx = int(''.join(reversed(X.read(4))).encode('hex'), 16) ^ ax bl = int(format(bx, 'X')[-2:], 16) X.seek(0x00) loop = path.getsize(xbtffile) data = list(X.read(loop)) X.close() di = 4 al = 0 loop -= 4 while loop > 0: dl = ord(data[di]) dl = dl ^ bl dl = dl - al if dl < 0: dl = dl ^ 0xFFFF dl = dl +1 dl = int(format(dl, 'X')[-2:], 16) data[di] = struct.pack("B", dl) al += 3 al += loop al = int(format(al, 'X')[-2:], 16) di += 1 loop -= 1 loop = 2 index = 4 scroller = "" while loop > 0: byte = data[index] if byte == "\0": byte = "" while byte: byte = data[index] if byte == "\0": byte = "" index += 1 if loop == 1: scroller = scroller + byte loop -= 1 return(data[index:]) def etmread(etmfile): filename, extension = path.splitext(etmfile) if extension.lower() == ".xbtf": data = xbtfread(etmfile) else: X = open(etmfile, 'rb') loop = path.getsize(etmfile) data = list(X.read(loop)) X.close() return(data) def getpointers(ETMdata): gptr_list = [] gtemp_ptr = ETMdata[ID:ID+2] gtemp_ptr = int(''.join(reversed(gtemp_ptr)).encode('hex'), 16) gptr_list.append(gtemp_ptr) table_ptr = ETMdata[Options:Options+2] table_ptr = int(''.join(reversed(table_ptr)).encode('hex'), 16) gtemp_ptr = ETMdata[table_ptr:table_ptr+2] gtemp_ptr = int(''.join(reversed(gtemp_ptr)).encode('hex'), 16) gptr_list.append(gtemp_ptr) table_ptr += 4 while gtemp_ptr: gtemp_ptr = ETMdata[table_ptr:table_ptr+2] gtemp_ptr = int(''.join(reversed(gtemp_ptr)).encode('hex'), 16) if gtemp_ptr == "\0": gtemp_ptr = "" if gtemp_ptr == 0: gtemp_ptr = "" if gtemp_ptr: gptr_list.append(gtemp_ptr) table_ptr += 4 return(gptr_list) def getetmtext(etmfile): ETMdata = etmread(etmfile) ptr_list = getpointers(ETMdata) text_list = [] TitleID = ETMdata[ptr_list[0]:ptr_list[0]+4] TitleID = ''.join(reversed(TitleID)).encode('hex') text_list.append(TitleID) for li_ptr in ptr_list[1:]: text = Ctyperead(ETMdata,li_ptr) if text: text_list.append(text) ETMdata = "" return(text_list) def getfilelist(): filelist = [] files = [f for f in os.listdir('.') if path.isfile(f)] for thisfile in files: filename, extension = path.splitext(thisfile) for thisext in filetypes: if extension.lower() == thisext: filelist.append(thisfile) filelist = sorted(filelist, key=str.lower) return(filelist) # ### CLASSES ### ### ### ### ### class mainwindow(object): """Main window class """ def __init__(self): self.etmfiles = getfilelist() def window(self): self.root = tk.Tk() self.root.title(programname+" v"+version+" "+status) # ### FRAMES ### ### ### ### ### ### ### ### ### ### ### leftframe = tk.Frame(self.root, bg=fbg) leftframe.pack(fill='y', side='left') rightframe = tk.Frame(self.root, bg=fbg) rightframe.pack(fill='both', expand='yes', side='right') # ### FRAME LEFT ### ### ### ### ### ### ### ### ### ### self.filelist = tk.Listbox( master = leftframe, width = 40, height = 32, bg = '#ffffff', fg='#000000', selectmode = "SINGLE" ) self.filelist.pack(fill='y', expand='yes', side='left') for item in self.etmfiles: self.filelist.insert("end", item) # ### FRAME RIGHT ### ### ### ### ### ### ### ### ### ### self.infobox = tk.Label( master = rightframe, width = 58, height = 20, bg = fbg, fg='#000000' ) self.infobox.pack(fill='both', expand='yes', side='top') self.filler = tk.Frame(self.root, bg=fbg) self.filler.pack(fill='both', side='top', expand='yes') self.delete = tk.Button(rightframe, text="Delete selected file", bg=fbg, fg='#000000') self.delete.pack(side='bottom', fill='x', expand=False) self.delete = tk.Button(rightframe, text="", bg=fbg, fg='#000000') self.delete.pack(side='bottom', fill='x', expand=False) self.dupcheck = tk.Button(rightframe, text="Check for duplicates", bg=fbg, fg='#000000') self.dupcheck.pack(side='bottom', fill='x', expand=False) self.rename = tk.Button(rightframe, text="Rename selected file", command=self.renameETMfile, bg=fbg, fg='#000000') self.rename.pack(side='bottom', fill='x', expand=False) self.rename = tk.Button(rightframe, text="Rename selected file from Title", command=self.renamefromfile, bg=fbg, fg='#000000') self.rename.pack(side='bottom', fill='x', expand=False) self.loadinfo = tk.Button(rightframe, text="Load file info", command=self.readfile, bg=fbg, fg='#000000') self.loadinfo.pack(side='bottom', fill='x', expand=False) self.root.mainloop() def readfile(self): self.text_list = getetmtext(self.etmfiles[self.filelist.curselection()[0]]) self.formatted = "TitleID: " + str(self.text_list[0]+"\n\n") self.formatted = self.formatted + "Game: " + str(self.text_list[1]+"\n\n") self.formatted = self.formatted + "Author: " + str(self.text_list[2]+"\n\n*OPTIONS*\n") self.text_list = self.text_list[3:] for line in self.text_list: self.formatted = self.formatted + str(line)+"\n" self.infobox['text'] = self.formatted def renameETMfile(self): self.index = self.filelist.curselection()[0] rnw = renamewindow(self.etmfiles[self.index],self) self.nameret = rnw.window() def renamefromfile(self): self.index = self.filelist.curselection()[0] self.filetorename = self.etmfiles[self.filelist.curselection()[0]] self.text_list = getetmtext(self.filetorename) self.filename, self.extension = path.splitext(self.filetorename) self.namefromfile = self.text_list[1].replace("+", "p")+self.extension self.namefromfile = self.namefromfile.replace(":", "") os.rename(self.filename+self.extension, self.namefromfile) self.filelist.delete(self.index) self.filelist.insert(self.index, self.namefromfile) # ### END WINDOW ### ### ### ### ### ### ### ### ### ### ### ### class renamewindow(object): def __init__(self,filename,parent): self.filename = filename self.newname = tk.StringVar() self.parent = parent def window(self): self.renroot = tk.Toplevel() self.renroot.title("Rename file:") self.mainframe = tk.Frame(self.renroot, bg=fbg) self.mainframe.pack(fill='both', expand=False, side='top') self.nameedit = tk.Entry(self.mainframe, bg='#ffffff', fg='#000000', textvariable=self.newname, width=60) self.nameedit.pack(side='left', expand=False) self.nameedit.insert(0, self.filename) self.okbutton = tk.Button(self.mainframe, text="OK", bg=fbg, fg='#000000', command=self.retnewname, width=8) self.okbutton.pack(side='left', expand=False) self.cancelbutton = tk.Button(self.mainframe, text="CANCEL", bg=fbg, fg='#000000', command=self.cancel, width=8) self.cancelbutton.pack(side='left', expand=False) self.renroot.mainloop() def cancel(self): self.renroot.destroy() return("") def retnewname(self): if self.newname.get() != self.filename: os.rename(self.filename, self.newname.get()) self.parent.filelist.delete(self.parent.index) self.parent.filelist.insert(self.parent.index, self.newname.get()) self.renroot.destroy() return("") if __name__ == "__main__": w = mainwindow() w.window()