1
Ромхакинг и программирование / Программирование редакторов для внесения каких-то изменений в ром.
« : Вчера в 22:25:36 »
да я тоже не понимаю зачем ты делаешь свой плагин к иде. но ты ведь его делаешь значит оно кому-то нужно. в смысле весь этот бред с костылями - ида, питон, плагин, эмулятор... бред собачий делай сразу единую ОДНУ прогу, без костылей и ошибок. сразу чтоб в одном флаконе было и эмулятор, и весь разбор, и нормальное сохранение, и сразу компилер, и чтоб все происходило в памяти, без создания временных файлов на диске. (главное конечно чтоб ошибок в коде не рожало. типа как сейчас прозевал разницу .s и .w кажись... где-то там... забыл где. jsr наверное. где джамп сам на себя какой-то корявый. и еще что-то там, уже всё и не упомню...)
Отвлеклись. Продолжаем упарываться. То что уже сделано, то есть общая конструкция - можно сказать это уже готовый патчер для рома. Типа нам что-то известно - адреса и значения, и этим проектом можно что-то там исправить по известным адресам, втуливая туда нужные исправленные значения. Однако продолжая тему GUI - хотелось бы сделать программу более вежливой, а именно добавить диалоги указания файла к примеру. Или сделать окошко для ввода ручками на теле программы значения какого-то параметра. И только потом уже делать сохранение.
В начале определяемся с внешним видом окна. Берем в руки пейнт и рисуем что мы там хотим увидеть. Думаю должно быть что-то типа такого:
На первоначальном этапе тип пусть так и остается long - 4 байта. А чтобы не держать файл рома открытым все это время - прочитаем его в образ в памяти и будем работать с ним оттуда, а не с жесткого диска. Типа если вдруг что-то пойдет не так, то оригинальный файл мы не повредим... точнее повредим конечно, но в момент записи. А до этого - чтение файла (ReadFile) будет вполне себе безопасным.
Память.
Чтобы с ней работать - нужно сначала её зарезервировать: AllocateMemory. Это как бы объяснить... у нас есть картина метр на метр. Чтобы с ней успешно работать - нужен стол в мастерской, размером не меньше метр на метр. Вот это резервирование и есть как бы создание стола, куда мы положим наш квадрат Малевича и будем с ним работать. А так запись и чтение происходит аналогично как в случае с файлом, только что главное нам не вылезьти за размеры этого стола, иначе наступит конец света. Так-же разница между работой с файлом и памятью заключается в том, что надо отдельно отслеживать позицию куда мы пишем или читаем из памяти. Если с файлом что-то читается, то позиция сама автоматически сдвигается дальше - это как глаз, который читает текст - то есть прочитал слово, а глаз сам прыгнул на начало следующего слова и читает дальше. Вот в случае с памятью это не работает и надо отдельно вести счетчик где мы читаем и работать уже с ним.
Еще там будут нюансы с окошком диалога выбора пути до нужного файла. Там можно будет выставить ограничение на показ файлов - вместо *.* поставить что-то конкретное. Хочу поставить *.bin чтобы показывались только ромы, без всяких прочих других файлов типа exe, картинок, музыки...
Для окошек ввода текста поставим ограничение на ввод больших букв (параметр #PB_String_UpperCase), чтобы было что-то типа 45A4C1 вместо 45a4c1... А окошку "значение из файла" дополнительно поставим флаг "только для чтения" #PB_String_ReadOnly.
Хм... пока писал код - понял, что нужна отдельно кнопка "считать". Чтобы после ввода адреса в окошко её нажать, чтобы система прочитала из памяти по нужном адресу и вывела в окошке. Старость
Отвлеклись. Продолжаем упарываться. То что уже сделано, то есть общая конструкция - можно сказать это уже готовый патчер для рома. Типа нам что-то известно - адреса и значения, и этим проектом можно что-то там исправить по известным адресам, втуливая туда нужные исправленные значения. Однако продолжая тему GUI - хотелось бы сделать программу более вежливой, а именно добавить диалоги указания файла к примеру. Или сделать окошко для ввода ручками на теле программы значения какого-то параметра. И только потом уже делать сохранение.
В начале определяемся с внешним видом окна. Берем в руки пейнт и рисуем что мы там хотим увидеть. Думаю должно быть что-то типа такого:
На первоначальном этапе тип пусть так и остается long - 4 байта. А чтобы не держать файл рома открытым все это время - прочитаем его в образ в памяти и будем работать с ним оттуда, а не с жесткого диска. Типа если вдруг что-то пойдет не так, то оригинальный файл мы не повредим... точнее повредим конечно, но в момент записи. А до этого - чтение файла (ReadFile) будет вполне себе безопасным.
Память.
Чтобы с ней работать - нужно сначала её зарезервировать: AllocateMemory. Это как бы объяснить... у нас есть картина метр на метр. Чтобы с ней успешно работать - нужен стол в мастерской, размером не меньше метр на метр. Вот это резервирование и есть как бы создание стола, куда мы положим наш квадрат Малевича и будем с ним работать. А так запись и чтение происходит аналогично как в случае с файлом, только что главное нам не вылезьти за размеры этого стола, иначе наступит конец света. Так-же разница между работой с файлом и памятью заключается в том, что надо отдельно отслеживать позицию куда мы пишем или читаем из памяти. Если с файлом что-то читается, то позиция сама автоматически сдвигается дальше - это как глаз, который читает текст - то есть прочитал слово, а глаз сам прыгнул на начало следующего слова и читает дальше. Вот в случае с памятью это не работает и надо отдельно вести счетчик где мы читаем и работать уже с ним.
Еще там будут нюансы с окошком диалога выбора пути до нужного файла. Там можно будет выставить ограничение на показ файлов - вместо *.* поставить что-то конкретное. Хочу поставить *.bin чтобы показывались только ромы, без всяких прочих других файлов типа exe, картинок, музыки...
Для окошек ввода текста поставим ограничение на ввод больших букв (параметр #PB_String_UpperCase), чтобы было что-то типа 45A4C1 вместо 45a4c1... А окошку "значение из файла" дополнительно поставим флаг "только для чтения" #PB_String_ReadOnly.
Хм... пока писал код - понял, что нужна отдельно кнопка "считать". Чтобы после ввода адреса в окошко её нажать, чтобы система прочитала из памяти по нужном адресу и вывела в окошке. Старость
Код: [Выделить]
; перечисление окон, гаджетов и файлов
Enumeration
#Window
#TextPutDoRoma
#StrokaPutDoRoma
#KnopkaPutDoRoma
#TextAdresParametra
#TextAdrerParametra2 ; значок доллара будет
#StrokaAdresParametra
#TextZnachenieParametra
#TextZnachenieParametra2 ; $
#StrokaZnachenieParametra
#ProchitatZnachenieParametra
#TextNovoeZnachenie
#TextNovoeZnachenie2 ; $
#StrokaNovoeZnachenie
#ZadatNovoeZnachenie
#File
EndEnumeration
; создание окна
If OpenWindow(#Window, 100, 100, 410, 155, "Я у мамы программист.", #PB_Window_MinimizeGadget | #PB_Window_ScreenCentered)
; рисуем гаджеты на окне
; текстовой с надписью путь до рома
TextGadget(#TextPutDoRoma, 10, 10, 200, 20, "путь до рома:")
; строчка, куда мы будем указывать путь, выбранный через кнопку "обзор"
StringGadget(#StrokaPutDoRoma, 10, 30, 330, 20, "", #PB_String_ReadOnly)
; сама кнопка обзор
ButtonGadget(#KnopkaPutDoRoma, 350, 30, 50, 20, "обзор")
; адрес параметра
TextGadget(#TextAdresParametra, 25, 70, 70, 20, "адр. парам.:")
; доллар. без америки никуда. 92 - чуть ниже чем 90, чтобы визуально доллар был на одной линии
TextGadget(#TextAdrerParametra2, 10, 92, 20, 20, "$")
; само окошко ввода адреса
StringGadget(#StrokaAdresParametra, 25, 90, 70, 20, "", #PB_String_UpperCase)
; значение из файла
TextGadget(#TextZnachenieParametra, 135, 70, 70, 20, "значение:")
; доллар.
TextGadget(#TextZnachenieParametra2, 120, 92, 20, 20, "$")
; окошко адреса из файла
StringGadget(#StrokaZnachenieParametra, 135, 90, 70, 20, "", #PB_String_UpperCase | #PB_String_ReadOnly)
; кнопка прочитать
ButtonGadget(#ProchitatZnachenieParametra, 80, 120, 70, 20, "прочитать")
; новое значение
TextGadget(#TextNovoeZnachenie, 250, 70, 70, 20, "новое знач.:")
; доллар.
TextGadget(#TextNovoeZnachenie2, 235, 92, 20, 20, "$")
; окошко нового значения
StringGadget(#StrokaNovoeZnachenie, 250, 90, 70, 20, "", #PB_String_UpperCase)
; кнопка задать
ButtonGadget(#ZadatNovoeZnachenie, 330, 90, 70, 20, "задать")
; бесконечный цикл обработки событий окна
Repeat
; определяем с чем именно мы имеем дело
Select WaitWindowEvent()
; событие касается какого-то гаджета
Case #PB_Event_Gadget
; раз событие касается гаджетов, то надо перебрать какой именно гаджет имеет это событие
Select EventGadget()
Case #KnopkaPutDoRoma
;{ событие на кнопке указания пути до рома
If EventType() = #PB_EventType_LeftClick
; если событие клик левой кнопкой мышки
; открываем диалог указания пути до файла рома
PutDoRoma$ = OpenFileRequester("укажите путь до файла рома", "", "Rom bin file | *.bin", 0)
If PutDoRoma$
; если путь все-таки был указан, то...
If ReadFile(#File, PutDoRoma$)
; если файл прочитался, то...
; в начале сбрасываем память, а то может мы уже второй ром открываем.
; то есть возможно в памяти уже висит старый ром,
; а значит надо освободить старую память
If *MemoryID
FreeMemory(*MemoryID)
*MemoryID = 0
EndIf
; далее зачистить окошки адреса и того старого значения. а так-же нового.
SetGadgetText(#StrokaAdresParametra, "")
SetGadgetText(#StrokaZnachenieParametra, "")
SetGadgetText(#StrokaNovoeZnachenie, "")
; получаем размер файла
length = Lof(#File)
If length
; если размер файла больше нуля.
; эта проверка на случай если файл поврежден.
; резервируем память по размеру файла
*MemoryID = AllocateMemory(length)
If *MemoryID
; если память зарезервировалась, то все хорошо. играем дальше.
; читаем файл с жесткого диска в подготовленную память
; то есть файл - #File, в какую память - *MemoryID, размер сколько читать - length.
ReadData(#File, *MemoryID, length)
; раз у нас есть чтение чего-то в память, значит делаем вывод что все хорошо
; указываем путь в окошке для пути
SetGadgetText(#StrokaPutDoRoma, PutDoRoma$)
Else
; какая-то ошибка с памятью. опять выводим окошко с ошибкой.
MessageRequester("ошибка", "проблема с резервированием памяти.")
EndIf
Else
; выходит размер файла все-таки 0. значит ошибка. вывести предупреждение.
MessageRequester("ошибка", "файл рома видимо поврежден и имеет размер 0 байт.")
EndIf
; закрываем файл
CloseFile(#File)
Else
; выходит файл прочитать не смогли
MessageRequester("ошибка", "файл не может быть прочитан. возможно занят другой программой.")
EndIf
EndIf
EndIf
;}
Case #ProchitatZnachenieParametra
;{ кнопка прочитать из памяти значение по какому-то адресу
If EventType() = #PB_EventType_LeftClick
; если событие клик левой кнопкой мышки
; надо удостоверится, что файл рома был считан и существует память, куда все записалось
If length And *MemoryID
; надо удостоверися, что у нас есть адрес откуда читать
Address$ = GetGadgetText(#StrokaAdresParametra)
If Address$
; надо перевести текст адреса в число
; и сравнить нет ли превышения размера рома length
; чтобы не начать читать за пределами нужной памяти
; к текстовому значению Address$ был прибавлен символ доллара,
; чтобы команда Val поняла, что текст это хекс, а не обычные десятичные числа
Address = Val("$" + Address$)
If Address < length
; все в порядке. адрес в нужных пределах рома
; просчитываем место в памяти, откуда следует читать
mem = *MemoryID + Address
; читаем 4 байта из памяти по одному байту и сдвигаем итоговое значение
Result = PeekA(mem) << 8
Result | PeekA(mem + 1)
Result << 8
Result | PeekA(mem + 2)
Result << 8
Result | PeekA(mem + 3)
; выводим в окошке на теле программы итоговое значение с переводом в хекс
SetGadgetText(#StrokaZnachenieParametra, Hex(Result, #PB_Long))
Else
; превышение. выдать предупреждение.
MessageRequester("ошибка", "вы пытаетесь прочитать за пределами файла рома.")
EndIf
Else
; нет адреса, откуда читать.
MessageRequester("ошибка", "кто-то забыл указать адрес откуда нам читать (1C английскими).")
EndIf
Else
; ром еще видимо незагружен
MessageRequester("ошибка", "ром еще не был загружен.")
EndIf
EndIf
;}
Case #ZadatNovoeZnachenie
;{ кнопка внести новое значение в файл
If EventType() = #PB_EventType_LeftClick
; если событие клик левой кнопкой мышки
; надо удостоверится, что файл рома был считан и существует память, куда все записалось
If length And *MemoryID
; надо удостоверися, что у нас есть адрес куда писать
Address$ = GetGadgetText(#StrokaAdresParametra)
If Address$
; надо перевести текст адреса в число
; и сравнить нет ли превышения размера рома length
; чтобы не начать писать за пределами нужной памяти
; к текстовому значению Address$ был прибавлен символ доллара,
; чтобы команда Val поняла, что текст это хекс, а не обычные десятичные числа
Address = Val("$" + Address$)
If Address < length
; все в порядке. адрес в нужных пределах рома
; просчитываем место в памяти, куда следует писать
mem = *MemoryID + Address
; проверяем есть ли какое-то число в окошке, которое нам надо записать в память
Zapis$ = GetGadgetText(#StrokaNovoeZnachenie)
; по старой схеме переводим текст в число, с учетом что это хекс, а не десятичное
Zapis = Val("$" + Zapis$)
; разбиваем значение на 4 байта
first.a = Zapis >> 24
secnd.a = Zapis >> 16
third.a = Zapis >> 8
fourd.a = Zapis
; пишем в память
PokeA(mem, first)
PokeA(mem + 1, secnd)
PokeA(mem + 2, third)
PokeA(mem + 3, fourd)
; вроде как в памяти у нас уже сидит новое значение.
; значит надо перезаписать весь файл целиком
; читаем путь до рома из окошка в программе
PutDoRoma2$ = GetGadgetText(#StrokaPutDoRoma)
If PutDoRoma2$
; перепроверяем что он действительно существует
; создаем файл по новой
If CreateFile(#File, PutDoRoma2$)
; пишем туда образ памяти
; то есть куда - #File, какая именно память - *MemoryID, размер сколько писать - length
WriteData(#File, *MemoryID, length)
; закрываем файл
CloseFile(#File)
MessageRequester("победа!", "вроде бы все записалось куда надо. надо открыть наш ром в хекс редакторе и посмотреть что там куда записалось.")
Else
; файл создать не получилось
MessageRequester("ошибка", "файл не создался. может быть открыт в другой программе или еще чего.")
EndIf
Else
MessageRequester("ошибка", "нет пути до файла рома.")
EndIf
Else
; превышение. выдать предупреждение.
MessageRequester("ошибка", "вы пытаетесь прочитать за пределами файла рома.")
EndIf
EndIf
Else
; ром еще видимо незагружен
MessageRequester("ошибка", "ром еще не был загружен.")
EndIf
EndIf
;}
EndSelect
; событие касается закрытия окна
Case #PB_Event_CloseWindow
qiut = 1
EndSelect
; цикл крутится, пока не сработает закрытие окна и соответственно qiut станет равен 1
Until qiut = 1
EndIf
; завершение программы
End