Author Id: 12296 Author Name: cosarara97 Post Content: Hola!!! Tienes una idea? Quieres hacer una herramienta pero no sabes como? Si? Pues que raro, no es que sea muy frecuente, lol Ok, ya he acabado mi chiste malo, ahora viene la intro del tutorial. Bueno, en este tutorial vamos a aprender a leer y modificar un ROM usando python, lo que nos permite crear herramientas en un lenguaje fácil de aprender como es python. [spoiler=abrelo si estas pensando: "pero oye, el python no es mas lento que otros lenguajes como el C?" ] Bueno, yo solo diré que para escribir en un rom de 16 MB (pokemon ruby) ha tardado tan poco que ni me he enterado que ya lo había hecho (Si, mucho menos de un segundo), y que el python es mucho mas fácil :) [/spoiler] En este tutorial no aprenderemos python. Este tutorial está orientado a gente que ya sabe un poco de python, pero no sabe como escribir bytes en un rom. Dificultad: Baja (Voy a poner ejemplos), si sabes python. Si conoces otros lenguajes de programación como C, java, o similares (HTML NO es un lenguaje de programación xD), sería media-baja, si nunca has programado pero eres inteligente, deberías poder entenderlo, y si no eres tan listo, pues ejem, piensa, luego busca en google y finalmente pregunta, ok? Ok, empezamos: Leer y modificar un rom con python NOTA: Todas las lineas que empiecen con import, solo hay que ponerlas una vez en todo el programa. Recomiendo escribirlos todos al principio. Leer el rom: Primero vamos a definir una variable que contendrá la ruta hasta el ROM: filename = "" El nombre del rom, o la ruta completa hasta el rom ejemplo1: filename = "mirom.gba" ejemplo2 (linux): filename = "/home/mi_usuario/asdf/mirom.gba" ejemplo3 (windows): filename = "C:\Users\usuario\asdf\mirom.gba" Luego abrimos el rom f = open(filename, "rb") f es el nombre de la variable que almacenará el la función open(), open es la función que abre el rom, filename es la variable que hemos definido antes y "rb" es la opción de leer bytes (r,read, y b,bytes). Luego almacenamos lo que el contenido del rom en una variable. fcontents = f.read() fcontents es la variable donde almacenamos todos los bytes del rom. (Va a ser de tipo string) [spoiler=Nota] Para evitar problemas (y ahorrar RAM), recomiendo cerrar ya el archivo con un f.close() , porque al haber copiado todo su contenido en fcontents, ya no lo necesitamos mas. [/spoiler] Si estás abriendo un ROM real y haces un "print fcontents" vas a morir (o tu PC va a morir), porque se escribirá todo el contenido del rom en tu terminal :P Si en lugar de un ROM de GBA estas usando un binario creado por ti en un editor HEX, puedes imprimirlo en pantalla de dos maneras diferentes: 1: print fcontents Si tu binario contiene 0011223344, va a escribir: '\x00\x11"3D' [spoiler=Explicación] En este momento, fcontents contiene datos en hex, así que al mostrarlo en pantalla, los que pueda (como ", 3 y D), los va a mostrar con su equivalente en ascii. La tabla es esta: [url=http://www.asciitable.com/index/asciifull.gif]tabla[/url] [/spoiler] 2: import binascii print binascii.hexlify(fcontents) Si tu binario contiene 0011223344, va a escribir: 0011223344 [spoiler=Explicación] Esto es lo mismo que el anterior, con la diferencia que formateamos el texto para que aparezca así: "0011223344", usando la función hexlify del modulo binascii, de esta manera: binascii.hexlify(variable_a_codificar) [/spoiler] NOTA: A partir de ahora, para decir "imprimir en pantalla" usaré el verbo inexistente "printar", que es mas corto :D Ok, ahora vamos a printar el byte que hay en un offset (decimal) en concreto: offsetdec = "123456" [spoiler=Explicación]Donde 123456 es el offset DECIMAL del byte a printar. Si quisiéramos ver lo que hay en offset 100000, escribiríamos: offsetdec = "100000"[/spoiler] import binascii print binascii.hexlify(fcontents[offsetdec]) [spoiler=Explicación] Recordad que si ya lo hemos hecho antes, no hace falta importar binascii, pero no pasa nada por hacerlo 2 veces ;D. Otra manera mas larga (pero también más entendible) de escribirlo sería: nuestro_byte_binario = fcontents[offsetdec] # almacenamos nuestro byte en una variable nuestro_byte_leible = binascii.hexlify(nuestro_byte_binario) # Lo almacenamos en otra convertido print nuestro_byte_leible # Lo escribimos [/spoiler] [spoiler=Explicación 2] Para entender como cogemos el byte de fcontents (la parte de "fcontents[offsetdec]"): Siempre que ponemos un numero entre "[]" al lado del nombre de una variable, nos estamos refiriendo a una posición dentro de esta. Por ejemplo: variable = "asdfghjkl" print variable[0] print variable[1] print variable[2] print variable[2] + variable[1] + variable[0] Nos escribiría en pantalla: a s d dsa [/spoiler] Ahora lo mismo usando un offset HEX: offsethex = "123456" [spoiler=Explicación]Donde 123456 es el offset HEXADECIMAL del byte a printar. Si quisiéramos ver lo que hay en offset 800000, escribiríamos: offsetdec = "800000"[/spoiler] import binascii print binascii.hexlify(fcontents[int(offsethex, 16)]) [spoiler=Explicación] Es igual que el anterior, con la diferencia que usamos un offset HEX, que convertimos a decimal con la función "int(offsethex, 16)" Ejemplo: Este programa: variable = "asdfghjklzxcvbnm" offsethex = "C" print variable[int(offsethex, 16)] que es lo mismo que esto: variable = "asdfghjklzxcvbnm" offsethex = "C" offset_convertido = int(offsethex, 16) print variable[offset_convertido] Nos imprimiría en pantalla esto: v [/spoiler] Y ahora vamos a printar de un offset (HEX) a otro offsethex1 = "800000" offsethex2 = "800100" [spoiler=Explicación]Donde 800000 es el offset hex del primer byte a printar, y 800100 el del último.[/spoiler] NOTA: El primero tiene que ser un numero mas pequeño que el segundo (lógico, no?) import binascii print binascii.hexlify(fcontents[int(offsethex1, 16):int(offsethex2, 16)]) [spoiler=Explicación] Pongo un ejemplo y seguro que lo entendéis: Este programa: variable = "asdfghjklzxcvbnm" offsethex1 = "1" offsethex2 = "C" offset_convertido1 = int(offsethex1, 16) offset_convertido2 = int(offsethex2, 16) print variable[offset_convertido1:offset_convertido2] Nos daría esto: sdfghjklzxc No lo has entendido? Bueno, lo explico. Si habéis leído todas las explicaciones que llevamos hasta ahora, ya sabréis que el numero entre "[" y "]" nos indica una posición dentro de la variable que tiene a su izquierda. Si no, leedlo. Bueno, pues si en lugar de un numero ponemos 2 separados por dos puntos (":"), nos estamos refiriendo a todo lo que hay entre el primero y el último. Como me explico mal, pongo más ejemplos: var = "buenos dias :D" print var print var[0:20] print var[0:5] print var[2:4] Daría esto: buenos dias :D buenos dias :D bueno en Si nos pasamos, como en: print var[0:20] Se escribe hasta el final. Por si alguien va MUY perdido, lol: En python, cualquier cosa se puede sustituir por una variable que lo contenga, siendo lo mismo print "hola" que variable = "hola" print variable [/spoiler] Modificar el rom (escribir): Necesitamos el texto que hay en el ROM, así que si no lo hemos hecho: filename = "Ruby.gba" [spoiler=Explicación]Donde Ruby.gba es la ruta hasta el ROM. Ejemplos: El nombre del rom, o la ruta completa hasta el rom ejemplo1: filename = "mirom.gba" ejemplo2 (linux): filename = "/home/mi_usuario/asdf/mirom.gba" ejemplo3 (windows): filename = "C:\Users\usuario\asdf\mirom.gba" [/spoiler] f = open(filename, "rb") fcontents = f.read() [spoiler=Explicación] Esto está explicado más arriba, ok? [/spoiler] Escribir un solo byte (algo así como el WBTO pero para el ROM xD): fwb = open(filename, "wb") [spoiler=Explicación] Volvemos a abrir el ROM, esta vez en modo de escritura. [/spoiler] newbyte = "2C" [spoiler=Explicación] Donde 2C es el byte en HEX a insertar. [/spoiler] hexoffset = "800500" [spoiler=Explicación] Donde 800500 es el offset donde insertaremos el byte (en hex). [/spoiler] import binascii fwb.write(fcontents[:int(hexoffset, 16)] + binascii.a2b_hex(newbyte) \ + fcontents[int(hexoffset, 16) + 1:]) [spoiler=Explicación] [spoiler=Sobre las "\"] La backslash ("\"), se usa para anular al cambio de linea, con lo que es lo mismo escribir: asdf = "123" que asdf = \ "123" [/spoiler] [spoiler=Sobre el resto] Esto es lo difícil: fwb.write(fcontents[:int(hexoffset, 16)] + binascii.a2b_hex(newbyte) \ + fcontents[int(hexoffset, 16) + 1:]) Vamos a hacerlo poco a poco: Lo que haremos será escribir en el rom, que hemos abierto en modo de escritura en la var "fwb", así que: fwb.write() Ahora lo llenamos con lo que queremos escribir fwb.write(inicio_del_rom + bytes + final_del_rom) Lo sustituimos por algo mas parecido al resultado final :) fwb.write(ROM[:offset] + bytes + ROM[offset + 1:]) [spoiler=explicación] "ROM[:offset]" significa, des del principio del ROM hasta el offset, y "ROM[offset:]" significa des del offset hasta el final del ROM. Si lo hiciéramos así, estaríamos añadiendo un byte al ROM y se nos desplazaría todo dejandolo inservible, por lo que en lugar de "ROM[offset:]" hacemos un "ROM[offset + 1:]", que escribe des de un byte (el que hemos escrito) después del offset hasta el final. [/spoiler] Y en realidad la variable no se llama ROM, sino dcontents, así que lo cambiamos: fwb.write(fcontents[:offset] + bytes + fcontents[offset + 1:]) Ahora codificamos los offsets y el byte. fwb.write(fcontents[:int(hexoffset, 16)] + binascii.a2b_hex(newbyte) \ + fcontents[int(hexoffset, 16) + 1:]) Y bueno, este es el resultado final: fwb.write(fcontents[:int(hexoffset, 16)] + binascii.a2b_hex(newbyte) \ + fcontents[int(hexoffset, 16) + 1:]) Pero para hacerlo mas fácil también se podría escribir así: offset = int(hexoffset, 16) inicio_del_rom = fcontents[:offset] final_del_rom = fcontents[offset + 1:] byte_codificado = binascii.a2b_hex(newbyte) newcontent = inicio_del_rom + byte_codificado + final_del_rom fwb.write(newcontent) [/spoiler] [/spoiler] Escribir tantos bytes como queramos fwb = open(filename, "wb") newbytes = "3322446655AA00FFDD" [spoiler=Explicación] Donde "3322446655AA00FFDD" son los bytes que vamos a escribir. [/spoiler] hexoffset = "800000" [spoiler=Explicación] Donde "800000" Es el offset donde vamos a insertar los bytes [/spoiler] import binascii fwb.write(fcontents[:int(hexoffset, 16)] + binascii.a2b_hex(newbytes) \ + fcontents[int(hexoffset, 16) + len(binascii.a2b_hex(newbytes)):]) [spoiler=Explicación] En este tenemos lo mismo que en el anterior con solo 2 pequeños cambios: 1 - En lugar de escribir un byte codificado escribimos varios 2 - Como escribimos varios, en lugar de desplazar el offset en el que empieza el final del ROM 1 byte, lo desplazamos la longitud de nuestra cadena de bytes, que calculamos con "len()" Separado sería: offset = int(hexoffset, 16)] principio_rom = fcontents[:offset] bytes_conv = binascii.a2b_hex(newbytes) longitud_newbytes = len(bytes_conv) final_rom = fcontents[offset + longitud_newbytes:] contenido_modif = principio_rom + bytes_conv + final_rom fwb.write(contenido_modif) [/spoiler] Ya hemos acabado, pero si has llegado hasta aquí leyéndolo todo, te agradecería que me dejaras un mensaje en el perfil con tu opinión y, si las tienes, dudas. Espero que ayude a la gente a crear sus herramientas :) Bye! [spoiler=EDITs Anteriores]EDIT: Código en colores (pero sin acentos) aquí: http://cosarara97.x10.mx/files/pythontuts/pythonROMs1.html EDIT 2: Adjunto tenéis el código en un txt con tabulaciones, y si teneis un buen editor de textos para programar lo podéis renombrar a *.py para tener colores :) EDIT 3: Había un error en el tutorial, que ya se ha corregido en el post pero aún no el la versión coloreada (el link) ni el adjunto. Cuando pueda lo corrijo, mientras leed del post :/ EDIT 4: El adjunto ya está arreglado EDIT 5: Dejo como adjunto un programa (un poco chapucero) para la linea de comandos como ejemplo, os recomiendo usarlo un poco y leer el código después de leer el tutorial para tener un ejemplo práctico. EDIT 6: He hecho otra versión del programa más elegante, está adjunta. [/spoiler] EDIT 7: He añadido muchos ejemplos y explicaciones :D EDIT 8: He añadido MÁS ejemplos y explicaciones.