Registrarse

[Otros] Modificar ROM en python

cosarara97

Dejad de cambiar de nick
Miembro de honor
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.

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 :)


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:
Código:
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
Código:
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.
Código:
fcontents = f.read()
fcontents es la variable donde almacenamos todos los bytes del rom. (Va a ser de tipo string)

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.

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:
Código:
print fcontents
Si tu binario contiene 0011223344, va a escribir:
'\x00\x11"3D'

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: tabla


2:
Código:
import binascii
print binascii.hexlify(fcontents)
Si tu binario contiene 0011223344, va a escribir:
0011223344

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:
Código:
binascii.hexlify(variable_a_codificar)

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:
Código:
offsetdec = "123456"
Donde 123456 es el offset DECIMAL del byte a printar. Si quisiéramos ver lo que hay en offset 100000, escribiríamos:
Código:
offsetdec = "100000"


Código:
import binascii
print binascii.hexlify(fcontents[offsetdec])
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:
Código:
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


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:
Código:
variable = "asdfghjkl"
print variable[0]
print variable[1]
print variable[2]
print variable[2] + variable[1] + variable[0]
Nos escribiría en pantalla:
Código:
a
s
d
dsa



Ahora lo mismo usando un offset HEX:
Código:
offsethex = "123456"
Donde 123456 es el offset HEXADECIMAL del byte a printar. Si quisiéramos ver lo que hay en offset 800000, escribiríamos:
Código:
offsetdec = "800000"


Código:
import binascii
print binascii.hexlify(fcontents[int(offsethex, 16)])
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:
Código:
variable = "asdfghjklzxcvbnm"
offsethex = "C"
print variable[int(offsethex, 16)]
que es lo mismo que esto:
Código:
variable = "asdfghjklzxcvbnm"
offsethex = "C"
offset_convertido = int(offsethex, 16)
print variable[offset_convertido]
Nos imprimiría en pantalla esto:
Código:
v


Y ahora vamos a printar de un offset (HEX) a otro
Código:
offsethex1 = "800000"
offsethex2 = "800100"
Donde 800000 es el offset hex del primer byte a printar, y 800100 el del último.

NOTA: El primero tiene que ser un numero mas
pequeño que el segundo (lógico, no?)

Código:
import binascii
print binascii.hexlify(fcontents[int(offsethex1, 16):int(offsethex2, 16)])
Pongo un ejemplo y seguro que lo entendéis:
Este programa:
Código:
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:
Código:
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:
Código:
var = "buenos dias :D"
print var
print var[0:20]
print var[0:5]
print var[2:4]
Daría esto:
Código:
buenos dias :D
buenos dias :D
bueno
en
Si nos pasamos, como en:
Código:
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
Código:
print "hola"
que
Código:
variable = "hola"
print variable


Modificar el rom (escribir):

Necesitamos el texto que hay en el ROM, así que si no lo hemos hecho:
Código:
filename = "Ruby.gba"
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"


Código:
f = open(filename, "rb")
fcontents = f.read()
Esto está explicado más arriba, ok?


Escribir un solo byte (algo así como el WBTO pero para el ROM xD):
Código:
fwb = open(filename, "wb")
Volvemos a abrir el ROM, esta vez en modo de escritura.

Código:
newbyte = "2C"
Donde 2C es el byte en HEX a insertar.


Código:
hexoffset = "800500"
Donde 800500 es el offset donde insertaremos el byte (en hex).


Código:
import binascii
fwb.write(fcontents[:int(hexoffset, 16)] + binascii.a2b_hex(newbyte) \
+ fcontents[int(hexoffset, 16) + 1:])
La backslash ("\"), se usa para anular al cambio de linea, con lo que es lo mismo escribir:
Código:
asdf = "123"
que
Código:
asdf = \
"123"

Esto es lo difícil:
Código:
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:
Código:
fwb.write()
Ahora lo llenamos con lo que queremos escribir
Código:
fwb.write(inicio_del_rom + bytes + final_del_rom)
Lo sustituimos por algo mas parecido al resultado final :)
Código:
fwb.write(ROM[:offset] + bytes + ROM[offset + 1:])
"ROM[:eek:ffset]" 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.

Y en realidad la variable no se llama ROM, sino dcontents, así que lo cambiamos:
Código:
fwb.write(fcontents[:offset] + bytes + fcontents[offset + 1:])
Ahora codificamos los offsets y el byte.
Código:
fwb.write(fcontents[:int(hexoffset, 16)] + binascii.a2b_hex(newbyte) \
+ fcontents[int(hexoffset, 16) + 1:])
Y bueno, este es el resultado final:
Código:
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í:
Código:
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)



Escribir tantos bytes como queramos
Código:
fwb = open(filename, "wb")
newbytes = "3322446655AA00FFDD"
Donde "3322446655AA00FFDD" son los bytes que vamos a escribir.


Código:
hexoffset = "800000"
Donde "800000" Es el offset donde vamos a insertar los bytes


Código:
import binascii
fwb.write(fcontents[:int(hexoffset, 16)] + binascii.a2b_hex(newbytes) \
+ fcontents[int(hexoffset, 16) + len(binascii.a2b_hex(newbytes)):])
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:

Código:
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)

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!



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.

EDIT 7: He añadido muchos ejemplos y explicaciones :D

EDIT 8: He añadido MÁS ejemplos y explicaciones.
 

cosarara97

Dejad de cambiar de nick
Miembro de honor
RE: [creación de herramientas] Modificar ROM en python

Bueno, he re-formateado todo el tutorial para que se entienda mejor, y he puesto un programa como ejemplo (adjunto).
Si alguien quiere que ponga un ejemplo más especifico, que amplíe el que hay, hacer una pregunta (no entiendo nada no me sirve) o ponga mas comentarios, que me deje un comentario en el perfil o enviadme un MP, y estaré encantado de ayudarle :D
 
RE: [creación de herramientas] Modificar ROM en python

muchas gracias a linuxeros como a mi nos sirve mucho, pero hay herramientas como en nse para linux?
bueno me despido
grax por el tuto:D
 

RogellParadox~

Usuario mítico
RE: [creación de herramientas] Modificar ROM en python

Sin duda, es de muy gran ayuda, sin hablar que es muy fácil hacerlo..
Como ya te dije, programé en Python un par de días y fue una buena experiencia.

Espero que hagas más, y que va siempre actualizando.
 

VGS

I Love Enchiladas ♥
RE: (ACT 15/10) [creación de herramientas] Modificar ROM en python

es una gran ayuda, lo entendi bien:) espero q sigas haciendo mas y mas.......
 
Arriba