Просмотр сообщений

В этом разделе можно просмотреть все сообщения, сделанные этим пользователем.


Сообщения - SeregaZ

Страницы: [1] 2 3 4 5 ... 79 Далее
1
Итак... начинаем медленное вкатывание в графоний. Данный пост будет состоять из двух частей. Первая - возможности PB, который не тащит и вторая - обсудим палитру Genesis/Megadrive. Хотя не думаю что там суть палитры будет разительно отличаться от других консолей.

Кто помнит школьные годы - настоящие! А не эти вот все... с виндовсами, интернетом и дотой 2 - то там на бейсике учитель заставлял рисовать точки, линии, квадраты, круги... и внезапно на нашем бейсике это все работает! Plot, Line, Box, Circle... все свое родное и теплое.

Рисовать PB может как в картинку, как в файл-картинку, в гаджет-канвас на окне программы, в текстуру. Нас интересует вывод в картинку и последующее отображение этой картинки в гаджете-картинке на окне программы. Каждая команда рисования имеет координаты x y куда собственно начинать рисовать элемент, имеет цвет RGB(0-255, 0-255, 0-255) и свои отдельные нюансы - типа если это линия, то координаты второй точки докуда надо рисовать линию, если циркле - радиус, и все такое прочее. Это самое прочее можно уточнить жмакнув на нужной команде рисования и нажав F1.

Вот значит мы создаем новый проект, туда втуливаем болванку пустого окна программы, налепляем туда гаджет картинки и пишем код для рисования. Пока я думаю сделать длинный прямоугольник и в нем квадратами рисовать разные цвета. Типа как слово в Поле Чудес, только что вместо черного - разноцветные квадратики. Для разноцветности пусть будет рандом от 0 до 255 для каждого из трех сегментов цвета RGB.


Enumeration
  #Window
 
  #GadgetKartinka
  #SamaKartinka
EndEnumeration

; в начале создаем само изображение, пока пустое. это будет черный длинный прямоугольник
CreateImage(#SamaKartinka, 200, 20)

; стартуем сам процесс рисования
; ставим вывод, что рисовать в картинку
If StartDrawing(ImageOutput(#SamaKartinka))
   
  ; сначала черный фон на всю картинку
  Box(0, 0, 200, 20, RGB(0, 0, 0)) ; это черный цвет. в принципе можно было просто 0 написать вместо RGB(0, 0, 0)
 
  x = 0
  ; в цикле стартуем прорисовку квадратиков с рандомным цветом, сдвигая координату Х вправо каждый раз
  For i = 1 To 10
    Box(x, 0, 20, 20, RGB(Random(255, 0), Random(255, 0), Random(255, 0)))
    x + 20
  Next
 
  ; остановка рисования
  StopDrawing()
EndIf

If OpenWindow(#Window, 100, 100, 220, 40, "Якубович под грибами", #PB_Window_MinimizeGadget | #PB_Window_ScreenCentered)
 
  ; добавляем гаджет-картинку, с подготовленным изображением
  ImageGadget(#GadgetKartinka, 10, 10, 200, 20, ImageID(#SamaKartinka))
 
  Repeat
    Select WaitWindowEvent()

      Case #PB_Event_CloseWindow
        qiut = 1
   
    EndSelect
  Until qiut = 1

EndIf

End

Уууууу... красоты получилось - не описуемой!




Все хорошо, да только приставка наша такое не может :) Вместо абы какого RGB цвета там могут быть только огрызки. Вместо 0-255 для каждого значения RGB там могут быть всего 8 значений:
0, 36, 72, 109, 145, 181, 219, 255 (плюс минус. может на 1-2 различаться. каждый источник утверждает что только у них самые точные значения цветов и поскольку источников тьма то и цифры могут отличаться. скажем не 181, а 180, 182 и так далее)
И как хочешь - так и выкручивайся. Печаль. А докучи там еще и не RGB - а BGR. Всего там могут быть 4 палитры по 15 цветов и шестнадцатый прозрачный. Если посмотреть наш любимый Zero Tolerance с помощью VDP просмоторщика, то палитра должна выглядеть так:



Жмакнем в этом самом VDP виевере Dump Pal, сохраним этот файл и будем его читать в нашей программе и рисовать соответствующие цвета на картинке.

Для хранения цветов думаю нам следует сделать сложный массив. Представим себе ексель таблицу. В нашей таблице будет 4 колонки и 16 строчек. Вот массив будет иметь эти самые 4 колонки и в каждой 16 ячеек, куда мы будем втуливать цвет. Сам цвет сначала надо будет конвертануть из Сеговского BGR в обычный компьютерный RGB, чтобы PB понял каким собственно цветом надо закрашивать нужный квадратик.

ну и потрадиции форум взбрыкнул и незахотел принимать всю портянку кода - прикладываю в виде архива:
* pal.zip (2.25 КБ - загружено 0 раз.)

в итоге получилась у нас такая замечательная пол литра... эээ... в смысле палитра:




Похоже?  :cool:

2
ну, к сожалению, ничего не могу про это сказать - у меня windows 7 на обычной 86, точнее 64 - не arm. программа на самом деле максимально простая. там обычное чтение файлов и текста и всё. есть конечно вшитые дллки - как bass.dll, потом для эмуляции звука ym2612, и еще одна dll для эмуляции драйвера звука - они могут брыкаться на что-то недостающее в системе. но в теории все должно запускаться без проблем и работать. пусть без звука, но все остальное должно.

3
а крестик если нажать? то есть не fix кнопку. old не значит что не будет работать. это просто проверка на версию. влиять на работоспособность она не должна. вот если бы там было написано что отсутствует, а не старая old - тогда может быть.

4
спаяй свой. наверное. не думаю что там что-то архи сложное.

у меня один геймпад еще тот, что в коробке был. ясное дело что второй. первый то давно умер. вместо первого использую рогатый, аля сониплейстешный. дополнительно там крестовину лезвием порезал, чтобы крестовина стала реально независимая.

еще в коробке всякие останки лежат. один причем большой такой... и два несовских. ясное дело что ни одного оригинального геймпада :) усе клоны, хоть и стародельные из 90х. как и обе тушки тоже клоны. хотя надо бы раскрутить да посмотреть чо там внутри.

5
должно работать все равно по идее... разве что звука не будет частично. по идее при запуске происходит распаковка этого басса в папку со сборщиком. раз этого не происходит - винда не дает видимо. запуск от администратора пробуй. я конечно еще схожу на сайт баса гляну мож там какие новые версии давно повыходили, а я на древних до сих пор сижу...

Добавлено позже:
и да, более новая версия есть на сайте. но чот поглядел... лень мне добавлять :) как вариант попробуй вручную её скачать и положить рядом с DUE, если винда не дает это сделать. в архиве куча файлов, но тебе только bass.dll нужна. правда редактор может взбрыкнуть, так как версия то другая, нежели в комплекте внутри редактора вшита... может удалить эту новую, а свою распаковать из себя опять винда не даст :)
https://www.un4seen.com/download.php?bass24

6
нифига се. оказывается даже несколько игр на восьмерых есть :) прикольно.

7
только в конце заметил, что с цветом количества патронов что-то не то :) ну да и фиг с ним. главное главный замысел показал.


в следующий раз уже будет рисование графики.

8
у меня нет такой кучи джойстиков. поставил 1 игрок как teamplayer. а 2 игроку назначил клавиши цифровые на направление и цифру 5 на стрельбу. все работает. в твоем случае может один джойстик все-таки дохлый? или ты чот не то переключил... или не переназначил клавиши. пусть пятый на клаве шпилит :) а 4 на джойстиках.

9
Поскольку мы создаем супер-мега программу, то немешало бы добавить функционала по менеджменту данных. Типа это будет некий лист со списком адресов и типов данных - переменная это или может быть файл с данными. Типа сначала все данные заносятся в лист, и потом по клику собирается новый ром со всеми изменениями, то есть файл будет сохранятся один раз, а не как сейчас перезапись всего файла при изменении всего одного какого-то параметра или данных. Докучи еще можно добавить галок - если галка отмечена, то изменение в ром попадает, и если галки нет, то соответственно игнорируем эту строку. Так-же, чтобы этот самый лист не вбивать каждый раз вручную, то сделаем сохранение конфиг файла. И соответственно при старте программы - загрузку этого конфига. Так-же сам ром файл - думаю не следует изменять оригинал. Будет создаваться новый ром файл, с припиской, скажем "_NEW". И кнопки "вписать" будут не писать в файл, как раньше, а лишь перекидывать текущие адреса и значения в таблицу.

Что нового используется в этой версии:
ListIconGadget. это гаджет для создания таблиц. там нужен будет спец режим с галками на строчках. включается флагом #PB_ListIcon_CheckBoxes в параметрах. столбики в эту таблицу добавляются путем использования AddGadgetColumn, где указывается ширина и текст, что будет отображаться в заголовке столбика. так-же будет еще флаг #PB_ListIcon_FullRowSelect - селект будет отображаться целиком на всю строку, а не на конкретную ячейку таблицы. так-же еще будет флаг #PB_ListIcon_AlwaysShowSelection - то есть не смотря что фокус с гаджета уходит (мы мышкой елозим на другом участке окна), то селект на нужной строке все равно будет продолжать висеть для визуального указания на какой строке мы сейчас находимся. и остался еще #PB_ListIcon_GridLines - рисует полоски для ячеек в таблице.
Chr. пишет символ по его коду. типа Chr(9) это табуляция.
AddGadgetItem. добавляет элемент, а в данном случае строку, в гаджет со списком. ячейки в строке разделяются специальным символом. например: "ячейка 1" + Chr(10) + "ячейка 2". получается одной строкой, с разделением внутри Chr(10) мы можем задать текст сразу в двух ячейках.
Str. переводит число в текст. Str(10) или Str($A) или Str(%1010) сделает из числа 10 - текст 10, который можно использовать с текстовыми переменными.
CountGadgetItems. пересчитывает количество строк в гаджете. в данном случае в листе.
FileSize. проверяет размер файла. если значение -1 = файла не существует. -2 это не файл, а папка.
WriteStringN. пишет построчно текст в файл. и сдвигает вывод на следующую строчку.
RunProgram. позволяет стартовать другие программы. здесь я хочу сделать функционал, чтобы после сборки нового рома он сразу запускался в эмуляторе. то есть будет запускаться эмулятор с указанием пути до нового ром файла для старта в эмуляторе.
StringField. разбивает текст по какому-то символу или слову. типа скажем пробел. тем самым фразу "привет мир" можно разделить отдельно по словам "привет" и "мир".

Итак, нужный функционал мы текстом описали - погнали гадить! ой... то есть кодить!

ога... разбежался я :) форум не хочет принимать простыню с кодом. придется прикладывать в виде файла-проекта для PB.

* main.zip (8.77 КБ - загружено 1 раз.)

Последующий пост планируется с ютубным видосом с практическими занятиями с нашей шедевральной супер программой. Поэтому КВЕСТ ДЛЯ РОМХАКЕРОВ: необходим адрес для Zero Tolerance, где лежит стартовое количество патронов для пистолета для персонажа-снайперши. Поэтому если кому не лень - было бы очень замечательно :)

10
у меня два Fusion и Gens r57shell mod - в каком-то из них действительно проблема была, и поэтому мы играли на втором... но не помню точно. скорей всего Fusion не работал.

11
какая еще такая быстрая скорость? всегда такая была. или какой глюк там у вас? можно разве что переключить страну - сейчас в эмуляторе скорей всего стоит NTSC. надо переключить на Европу - PAL - должно стать чуть медленнее. Батя тоже с племяшками не осилил. посидел два боя, потом обиделся, что не улавливает что ему надо делать и ушел :) ему даже пал слишком быстро.

12
Прежде следующего "урока" думаю было бы не плохо обсудить менеджмент дизайна окна. В нашем случае уже как бы есть два раздельных сегмента функционала программы - верхний, где можно задать какое-то одно значение в роме и нижний, где можно вписать какой-то файл во внутрь рома. В случае если я хочу поменять их местами - скажем сегмент с памятью поместить наверх, а с отдельными значениями - вниз, то придется каждому гаджету менять вручную координаты. Как бы с одной стороны да, там в PB вроде как есть встроенный редактор форм, где мышкой можно таскать кнопки и прочие гаджеты и размещать как душе угодно, но я им не пользуюсь. Предпочитаю указывать координаты как переменные x и y, где внутри сегмента есть базовый стартовый гаджет с x и y, а остальные имеют координаты x + 10, y + 10 скажем, то есть рисуются относительно стартового гаджета на окне программы. Тогда эти самые сегменты функционала можно сразу двигать оптом, меняя координаты только в одном месте. Предлагаю от воды перейти к практике и рассмотреть эту мысль в коде.

* пришлось код снести, так как не дает постануть вторую часть кода, с уже с добавленным элементом. чушь кароче с форумом :) лимит на количество строк в одном посте видимо.

Вы можете возразить:
- Простите, но ведь здесь нет никакой разницы! Что за бред вы тут втираете?!?!?
- А вы попробуйте добавить еще один фрейм - рамку - Frame - которая бы визуально обрамляла верхнюю часть программы для красивости. придется двигать все, уже нарисованные гаджеты на окне программы. Теперь же, когда у нас все через переменные x и y - достаточно будет в одном месте вбить другие координаты и сразу весь этот блок сдвинется на нужное место. Как в коде ниже:

; перечисление окон, гаджетов и файлов
Enumeration
  #Window
 
  #TextPutDoRoma
  #StrokaPutDoRoma
  #KnopkaPutDoRoma
 
  #Frame2
 
  #TextAdresParametra
  #TextAdrerParametra2 ; значок доллара будет
  #StrokaAdresParametra
 
  #TextZnachenieParametra
  #TextZnachenieParametra2 ; $ 
  #StrokaZnachenieParametra
  #ProchitatZnachenieParametra
 
  #TextNovoeZnachenie
  #TextNovoeZnachenie2 ; $ 
  #StrokaNovoeZnachenie
  #ZadatNovoeZnachenie 
 
  #TextTipParametra
  #LongTipParametra
  #WordTipParametra
  #ByteTipParametra
 
  #File
 
  #Frame
  #PatchText
  #PatchPut
  #PatchObzor
  #PatchTextAddres
  #PatchTextAddres2
  #PatchAddres
  #PatchKnopka
EndEnumeration


; создание окна
If OpenWindow(#Window, 100, 100, 430, 370, "Я у мамы программист.", #PB_Window_MinimizeGadget | #PB_Window_ScreenCentered)
 
  ; рисуем гаджеты на окне
 
  ; стартовые х и у, откуда будут танцевать все гаджеты на окне
  x = 10
  y = 10
  ; текстовой с надписью путь до рома
  TextGadget(#TextPutDoRoma, x, y, 200, 20, "путь до рома:") 
  ; строчка, куда мы будем указывать путь, выбранный через кнопку "обзор"
  StringGadget(#StrokaPutDoRoma, x, y + 20, 330, 20, "", #PB_String_ReadOnly) 
  ; сама кнопка обзор
  ButtonGadget(#KnopkaPutDoRoma, x + 340, y + 20, 50, 20, "обзор")
 
  ; рамка
  FrameGadget(#Frame2, x, y + 60, 410, 155, "отдельное значение")
  ; чтобы сдвинуть ниже и влево прибавляем к х и у
  x + 10
  y + 20
  ; адрес параметра
  TextGadget(#TextAdresParametra, x + 15, y + 60, 70, 20, "адр. парам.:")
  ; доллар. без америки никуда. 92 - чуть ниже чем 90, чтобы визуально доллар был на одной линии
  TextGadget(#TextAdrerParametra2, x, y + 82, 20, 20, "$")
  ; само окошко ввода адреса
  StringGadget(#StrokaAdresParametra, x + 15, y + 80, 70, 20, "", #PB_String_UpperCase)
 
  ; значение из файла
  TextGadget(#TextZnachenieParametra, x + 125, y + 60, 70, 20, "значение:")
  ; доллар.
  TextGadget(#TextZnachenieParametra2, x + 110, y + 82, 20, 20, "$")
  ; окошко значение с адреса из файла
  StringGadget(#StrokaZnachenieParametra, x + 125, y + 80, 70, 20, "", #PB_String_UpperCase | #PB_String_ReadOnly)
 
  ; строчка текста тип параметра
  TextGadget(#TextTipParametra, x, y + 110, 70, 20, "тип:")
 
 
  OptionGadget(#LongTipParametra, x, y + 130, 100, 20, "long (4 байта)")
  OptionGadget(#WordTipParametra, x + 110, y + 130, 100, 20, "word (2 байта)")
  OptionGadget(#ByteTipParametra, x + 230, y + 130, 100, 20, "byte (1 байта)")
  SetGadgetState(#LongTipParametra, 1)
  TipPeremennoy = 4
 
  ; кнопка прочитать
  ButtonGadget(#ProchitatZnachenieParametra, x + 70, y + 160, 70, 20, "прочитать")
 
 
  ; новое значение
  TextGadget(#TextNovoeZnachenie, x + 240, y + 60, 70, 20, "новое знач.:")
  ; доллар.
  TextGadget(#TextNovoeZnachenie2, x + 225, y + 82, 20, 20, "$")
  ; окошко нового значения
  StringGadget(#StrokaNovoeZnachenie, x + 240, y + 80, 70, 20, "", #PB_String_UpperCase)
  ; кнопка задать
  ButtonGadget(#ZadatNovoeZnachenie, x + 320, y + 80, 70, 20, "задать")
 
  x - 10 ; возвращаем как было
  FrameGadget(#Frame, x, y + 200, 410, 130, "данные")
    TextGadget(#PatchText, x + 15, y + 220, 100, 20, "файл с данными:")
    StringGadget(#PatchPut, x + 15, y + 240, 300, 20, "", #PB_String_ReadOnly)
    ButtonGadget(#PatchObzor, x + 325, y + 240, 50, 20, "обзор")
   
    TextGadget(#PatchTextAddres, x + 25, y + 270, 200, 20, "писать начиная с адреса:")
    TextGadget(#PatchTextAddres2, x + 15, y + 292, 20, 20, "$")
    StringGadget(#PatchAddres, x + 25, y + 290, 100, 20, "", #PB_String_UpperCase)
    ButtonGadget(#PatchKnopka, x + 140, y + 290, 100, 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
                     
                     ; в зависимости от выбранного типа переменной будем читать различное количество байт
                     Select TipPeremennoy
                       Case 4 ; long
                         ; читаем 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))
                         
                       Case 2 ; word
                         ; читаем 2 байта из памяти по одному байту и сдвигаем итоговое значение
                         Result = PeekA(mem) << 8
                         Result | PeekA(mem + 1)
                         
                         ; выводим в окошке на теле программы итоговое значение с переводом в хекс
                         SetGadgetText(#StrokaZnachenieParametra, Hex(Result, #PB_Word))
                       Case 1 ; byte
                         ; просто читаем 1 байт с нужного адреса
                         Result = PeekA(mem)
                         
                         ; выводим в окошке на теле программы итоговое значение с переводом в хекс
                         SetGadgetText(#StrokaZnachenieParametra, Hex(Result, #PB_Byte))
                     EndSelect
                   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$)
                     
                     ; в зависимости от типа переменной запись будет происходить по разному
                     Select TipPeremennoy
                       Case 4 ; long
                         ; разбиваем значение на 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)
                       Case 2 ; word
                         ; разбиваем значение на 2 байта
                         first.a = Zapis >> 8
                         secnd.a = Zapis
                         PokeA(mem, first)
                         PokeA(mem + 1, secnd)
                       Case 1 ; byte
                         PokeA(mem, Zapis)
                     EndSelect
                         
                     ; вроде как в памяти у нас уже сидит новое значение.
                     ; значит надо перезаписать весь файл целиком
                     ; читаем путь до рома из окошка в программе
                     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
             ;}
             
           Case #LongTipParametra
             ; переключим флаг, сигнализирующий какая переменная нам нужна
             TipPeremennoy = 4
             ; стираем текст из окошка значения адреса
             SetGadgetText(#StrokaZnachenieParametra, "")
             
           Case #WordTipParametra
             TipPeremennoy = 2
             ; стираем текст из окошка значения адреса
             SetGadgetText(#StrokaZnachenieParametra, "")
             
           Case #ByteTipParametra
             TipPeremennoy = 1
             ; стираем текст из окошка значения адреса
             SetGadgetText(#StrokaZnachenieParametra, "")
             
           Case #PatchObzor
             ;{ указываем путь до файла с данными, который надо вписать в главный ром
             
             If EventType() = #PB_EventType_LeftClick
               ; если событие клик левой кнопкой мышки
               
               ; открываем диалог указания пути до файла рома
               PutDoFile$ = OpenFileRequester("укажите путь до файла, который нужно вписать в ром", "", "Rom bin file | *.bin", 0)
               If PutDoFile$
                 ; если путь все-таки был указан, то...
                 
                 If ReadFile(#File, PutDoFile$)
                   ; если файл прочитался, то...
                   
                   ; в начале сбрасываем память, а то может мы уже второй раз открываем.
                   ; то есть возможно в памяти уже висит старый образ файла,
                   ; а значит надо освободить старую память
                   If *MemoryID2
                     FreeMemory(*MemoryID2)
                     *MemoryID2 = 0
                   EndIf
                   
                   ; получаем размер файла
                   length2 = Lof(#File)
                   
                   If length2
                     ; если размер файла больше нуля.
                     ; эта проверка на случай если файл поврежден.
                     
                     ; резервируем память по размеру файла
                     *MemoryID2 = AllocateMemory(length2)
                     
                     If *MemoryID2
                       ; если память зарезервировалась, то все хорошо.
                       
                       ; читаем файл с жесткого диска в подготовленную память
                       ReadData(#File, *MemoryID2, length2)
                       
                       ; далее вписываем путь до файла в окошко пути.
                       SetGadgetText(#PatchPut, PutDoFile$)
                       
                     Else
                       ; какая-то ошибка с памятью. опять выводим окошко с ошибкой.
                       MessageRequester("ошибка", "проблема с резервированием памяти.")                       
                     EndIf
                   
                   Else
                     ; выходит размер файла все-таки 0. значит ошибка. вывести предупреждение.
                     MessageRequester("ошибка", "файл видимо поврежден и имеет размер 0 байт.")
                   EndIf
                   

                   ; закрываем файл
                   CloseFile(#File)
                   
                 Else
                   ; выходит файл прочитать не смогли
                   MessageRequester("ошибка", "файл не может быть прочитан. возможно занят другой программой.")
                 EndIf                 
                 
               EndIf
               
             EndIf
             ;}
             
           Case #PatchKnopka
             ;{ кнопка вписывания файла с данными в итоговый ром
             
             If EventType() = #PB_EventType_LeftClick
               ; если событие клик левой кнопкой мышки
               
               ; проверить указан ли ром файл и записан ли он в память
               RomFile$ = GetGadgetText(#StrokaPutDoRoma)
               If RomFile$ And *MemoryID And length
                 ; то есть существует и путь до файла рома и образ в памяти и размер больше 0
                 
                 ; проверить указан ли путь до файла с данными
                 PatchFile$ = GetGadgetText(#PatchPut)
                 If PatchFile$ And *MemoryID2 And length2
                   ; то есть существует и путь до файла с данными и образ в памяти и размер больше 0
                   
                   ; проверить есть ли у нас вбитый пользователем адрес куда начать писать файл
                   StartAddress$ = GetGadgetText(#PatchAddres)
                   If StartAddress$
                 
                     ; переводим текст в число
                     StartAddress = Val("$" + StartAddress$)
                 
                     ; расчет адреса в памяти нашей программы, куда надо вписать файл с данными
                     mem = *MemoryID + StartAddress
                 
                     ; непосредственно вписывание в памяти
                     CopyMemory(*MemoryID2, mem, length2)
                 
                     ; когда память уже подготовлена, то есть данные куда надо уже вписаны - создаем файл рома
                     If CreateFile(#File, RomFile$)
                       
                       ; пишем туда образ памяти
                       WriteData(#File, *MemoryID, length)
                       
                       CloseFile(#File)
                       
                       MessageRequester("опять победа, чо...", "все прошло успешно. перепроверяем что получилось в хекс редакторе.")
                       
                     Else
                       ; ошибка создания файла рома
                       MessageRequester("ошибка", "создать патченный ром файл не удалось. видимо этот файл открыт в другой программе.")
                     EndIf
                 
               Else
                 ; адреса нет. выводим ошибку.
                 MessageRequester("ашипка", "вы не указали стартовый адрес откуда начинать запись файла с данными в роме.")
               EndIf
                   
                 Else
                   ; ошибка. нет или пути до рома или образ в памяти отсутствует
                   MessageRequester("ошибка", "путь до файла с данными не указан, либо проблема с работой памяти программы, либо размер файла 0.")
                 EndIf
                   
               Else
                 ; ошибка. нет или пути до рома или образ в памяти отсутствует
                 MessageRequester("ошибка", "путь до файла рома не указан, либо проблема с работой памяти программы, либо размер файла 0.")
               EndIf

             EndIf
             ;}
             
         EndSelect
         
       ; событие касается закрытия окна
       Case #PB_Event_CloseWindow
         qiut = 1
   
     EndSelect
     
     ; цикл крутится, пока не сработает закрытие окна и соответственно qiut станет равен 1
   Until qiut = 1
   

EndIf


; завершение программы
End


13
Как бы с втуливанием 4-2-1 байтных значений разобрались. А что если данных куда больше, чем одно значение? Для мегадрайвового случая есть такая замечательная штука как ASM68K.exe и в принципе можно втулить эти самые данные через неё. Создаем текстовой файл - например code.txt, и пишем туда следующий текст:
org 0
incbin "testfile.bin" ; оригинальный ром.

org $1C ; это адрес, где сидит наше long значение $12345678
incbin "file.bin" ; файл с данными которые надо вписать. его сейчас конечно не существует, но предположим он как суслик - есть и занимает 10 байт к примеру.

После создаем build.bat файл, куда пишем что-то типа:
asm68k /p  code.txt,NEW_ROM.bin
pause

Что здесь что:
org - это аналогично нашему PB'шному FileSeek - прыжок внутри файла по нужному адресу
incbin - включить в итоговый файл данные из файла, который здесь будет указан
asm68k с параметрами - собственно вызов ассемблера, который соберет файл и на выходе получится NEW_ROM.bin
pause - черное дос-вида окошко не закроется после выполнения программы, а продолжит висеть, чтобы бы могли прочитать результат - успешно ли или может какая ошибка. и если ошибка, то где именно.

В итоговом файле, начиная с указанного адреса $1C будут находится данные из файла file.bin. То есть это опять таки можно назвать своего рода патчером. Вот для расширения функционала нашей программы предлагаю это добавить.

Что нового будет использовано в коде:
гаджет Frame - визуальная рамка, чтобы разделить функционал программы. Типа сверху втуливание одного значения, а снизу вписывание целого файла.
указатель на образ памяти и отдельная переменная для указания размера файла, который надо вписать в итоговый ром - *MemoryID2 и length2

Значит по схеме выше сначала вписываем файл в память, а после пишем память в память - хотя при такой записи могут быть нюансы, но о них позже. Так-же понадобится скачать и распаковать из архива файл file.bin в нашу папку с проектом - мы для тестов будет указывать именно его для вписывания в наш ром файл.

; перечисление окон, гаджетов и файлов
Enumeration
  #Window
 
  #TextPutDoRoma
  #StrokaPutDoRoma
  #KnopkaPutDoRoma
 
  #TextAdresParametra
  #TextAdrerParametra2 ; значок доллара будет
  #StrokaAdresParametra
 
  #TextZnachenieParametra
  #TextZnachenieParametra2 ; $ 
  #StrokaZnachenieParametra
  #ProchitatZnachenieParametra
 
  #TextNovoeZnachenie
  #TextNovoeZnachenie2 ; $ 
  #StrokaNovoeZnachenie
  #ZadatNovoeZnachenie 
 
  #TextTipParametra
  #LongTipParametra
  #WordTipParametra
  #ByteTipParametra
 
  #File
 
  #Frame
  #PatchText
  #PatchPut
  #PatchObzor
  #PatchTextAddres
  #PatchTextAddres2
  #PatchAddres
  #PatchKnopka
EndEnumeration


; создание окна
If OpenWindow(#Window, 100, 100, 410, 350, "Я у мамы программист.", #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)
 
  ; строчка текста тип параметра
  TextGadget(#TextTipParametra, 10, 120, 70, 20, "тип:")
 
 
  OptionGadget(#LongTipParametra, 10, 140, 100, 20, "long (4 байта)")
  OptionGadget(#WordTipParametra, 120, 140, 100, 20, "word (2 байта)")
  OptionGadget(#ByteTipParametra, 240, 140, 100, 20, "byte (1 байта)")
  SetGadgetState(#LongTipParametra, 1)
  TipPeremennoy = 4
 
  ; кнопка прочитать
  ButtonGadget(#ProchitatZnachenieParametra, 80, 170, 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, "задать")
 
  FrameGadget(#Frame, 10, 210, 390, 130, "данные")
    TextGadget(#PatchText, 25, 230, 100, 20, "файл с данными:")
    StringGadget(#PatchPut, 25, 250, 300, 20, "", #PB_String_ReadOnly)
    ButtonGadget(#PatchObzor, 335, 250, 50, 20, "обзор")
   
    TextGadget(#PatchTextAddres, 35, 280, 200, 20, "писать начиная с адреса:")
    TextGadget(#PatchTextAddres2, 25, 302, 20, 20, "$")
    StringGadget(#PatchAddres, 35, 300, 100, 20, "", #PB_String_UpperCase)
    ButtonGadget(#PatchKnopka, 150, 300, 100, 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
                     
                     ; в зависимости от выбранного типа переменной будем читать различное количество байт
                     Select TipPeremennoy
                       Case 4 ; long
                         ; читаем 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))
                         
                       Case 2 ; word
                         ; читаем 2 байта из памяти по одному байту и сдвигаем итоговое значение
                         Result = PeekA(mem) << 8
                         Result | PeekA(mem + 1)
                         
                         ; выводим в окошке на теле программы итоговое значение с переводом в хекс
                         SetGadgetText(#StrokaZnachenieParametra, Hex(Result, #PB_Word))
                       Case 1 ; byte
                         ; просто читаем 1 байт с нужного адреса
                         Result = PeekA(mem)
                         
                         ; выводим в окошке на теле программы итоговое значение с переводом в хекс
                         SetGadgetText(#StrokaZnachenieParametra, Hex(Result, #PB_Byte))
                     EndSelect
                   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$)
                     
                     ; в зависимости от типа переменной запись будет происходить по разному
                     Select TipPeremennoy
                       Case 4 ; long
                         ; разбиваем значение на 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)
                       Case 2 ; word
                         ; разбиваем значение на 2 байта
                         first.a = Zapis >> 8
                         secnd.a = Zapis
                         PokeA(mem, first)
                         PokeA(mem + 1, secnd)
                       Case 1 ; byte
                         PokeA(mem, Zapis)
                     EndSelect
                         
                     ; вроде как в памяти у нас уже сидит новое значение.
                     ; значит надо перезаписать весь файл целиком
                     ; читаем путь до рома из окошка в программе
                     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
             ;}
             
           Case #LongTipParametra
             ; переключим флаг, сигнализирующий какая переменная нам нужна
             TipPeremennoy = 4
             ; стираем текст из окошка значения адреса
             SetGadgetText(#StrokaZnachenieParametra, "")
             
           Case #WordTipParametra
             TipPeremennoy = 2
             ; стираем текст из окошка значения адреса
             SetGadgetText(#StrokaZnachenieParametra, "")
             
           Case #ByteTipParametra
             TipPeremennoy = 1
             ; стираем текст из окошка значения адреса
             SetGadgetText(#StrokaZnachenieParametra, "")
             
           Case #PatchObzor
             ;{ указываем путь до файла с данными, который надо вписать в главный ром
             
             If EventType() = #PB_EventType_LeftClick
               ; если событие клик левой кнопкой мышки
               
               ; открываем диалог указания пути до файла рома
               PutDoFile$ = OpenFileRequester("укажите путь до файла, который нужно вписать в ром", "", "Rom bin file | *.bin", 0)
               If PutDoFile$
                 ; если путь все-таки был указан, то...
                 
                 If ReadFile(#File, PutDoFile$)
                   ; если файл прочитался, то...
                   
                   ; в начале сбрасываем память, а то может мы уже второй раз открываем.
                   ; то есть возможно в памяти уже висит старый образ файла,
                   ; а значит надо освободить старую память
                   If *MemoryID2
                     FreeMemory(*MemoryID2)
                     *MemoryID2 = 0
                   EndIf
                   
                   ; получаем размер файла
                   length2 = Lof(#File)
                   
                   If length2
                     ; если размер файла больше нуля.
                     ; эта проверка на случай если файл поврежден.
                     
                     ; резервируем память по размеру файла
                     *MemoryID2 = AllocateMemory(length2)
                     
                     If *MemoryID2
                       ; если память зарезервировалась, то все хорошо.
                       
                       ; читаем файл с жесткого диска в подготовленную память
                       ReadData(#File, *MemoryID2, length2)
                       
                       ; далее вписываем путь до файла в окошко пути.
                       SetGadgetText(#PatchPut, PutDoFile$)
                       
                     Else
                       ; какая-то ошибка с памятью. опять выводим окошко с ошибкой.
                       MessageRequester("ошибка", "проблема с резервированием памяти.")                       
                     EndIf
                   
                   Else
                     ; выходит размер файла все-таки 0. значит ошибка. вывести предупреждение.
                     MessageRequester("ошибка", "файл видимо поврежден и имеет размер 0 байт.")
                   EndIf
                   

                   ; закрываем файл
                   CloseFile(#File)
                   
                 Else
                   ; выходит файл прочитать не смогли
                   MessageRequester("ошибка", "файл не может быть прочитан. возможно занят другой программой.")
                 EndIf                 
                 
               EndIf
               
             EndIf
             ;}
             
           Case #PatchKnopka
             ;{ кнопка вписывания файла с данными в итоговый ром
             
             If EventType() = #PB_EventType_LeftClick
               ; если событие клик левой кнопкой мышки
               
               ; проверить указан ли ром файл и записан ли он в память
               RomFile$ = GetGadgetText(#StrokaPutDoRoma)
               If RomFile$ And *MemoryID And length
                 ; то есть существует и путь до файла рома и образ в памяти и размер больше 0
                 
                 ; проверить указан ли путь до файла с данными
                 PatchFile$ = GetGadgetText(#PatchPut)
                 If PatchFile$ And *MemoryID2 And length2
                   ; то есть существует и путь до файла с данными и образ в памяти и размер больше 0
                   
                   ; проверить есть ли у нас вбитый пользователем адрес куда начать писать файл
                   StartAddress$ = GetGadgetText(#PatchAddres)
                   If StartAddress$
                 
                     ; переводим текст в число
                     StartAddress = Val("$" + StartAddress$)
                 
                     ; расчет адреса в памяти нашей программы, куда надо вписать файл с данными
                     mem = *MemoryID + StartAddress
                 
                     ; непосредственно вписывание в памяти
                     CopyMemory(*MemoryID2, mem, length2)
                 
                     ; когда память уже подготовлена, то есть данные куда надо уже вписаны - создаем файл рома
                     If CreateFile(#File, RomFile$)
                       
                       ; пишем туда образ памяти
                       WriteData(#File, *MemoryID, length)
                       
                       CloseFile(#File)
                       
                       MessageRequester("опять победа, чо...", "все прошло успешно. перепроверяем что получилось в хекс редакторе.")
                       
                     Else
                       ; ошибка создания файла рома
                       MessageRequester("ошибка", "создать патченный ром файл не удалось. видимо этот файл открыт в другой программе.")
                     EndIf
                 
               Else
                 ; адреса нет. выводим ошибку.
                 MessageRequester("ашипка", "вы не указали стартовый адрес откуда начинать запись файла с данными в роме.")
               EndIf
                   
                 Else
                   ; ошибка. нет или пути до рома или образ в памяти отсутствует
                   MessageRequester("ошибка", "путь до файла с данными не указан, либо проблема с работой памяти программы, либо размер файла 0.")
                 EndIf
                   
               Else
                 ; ошибка. нет или пути до рома или образ в памяти отсутствует
                 MessageRequester("ошибка", "путь до файла рома не указан, либо проблема с работой памяти программы, либо размер файла 0.")
               EndIf

             EndIf
             ;}
             
         EndSelect
         
       ; событие касается закрытия окна
       Case #PB_Event_CloseWindow
         qiut = 1
   
     EndSelect
     
     ; цикл крутится, пока не сработает закрытие окна и соответственно qiut станет равен 1
   Until qiut = 1
   

EndIf


; завершение программы
End


Теперь что касается нюансов вписывания память в память. Память для PB это такая нежная штука... чуть чих влево или вправо - все вылетает к чертовой бабушке :) Если наш файл патча маленький и мы пишем куда-то в середину файла рома, как в моем примере, то проблем не будет. Но вот если наш патч файл вылезет за пределы оригинального размера памяти рома, то этот самый вылет и произойдет.


Этот нюанс работы в памятью надо иметь ввиду и продумывать заранее, а не как я :) То есть надо добавить всякие дополнительные проверки размеров. Типа рассчитывать начальный адрес + размер файла патча = не вылезет ли за пределы оригинального размера рома? Но это уже другая история :)

Добавлено позже:
ну и да... у нас должен получится новый ром файл с таким содержимым:

14
А. ромхакер не всегда программист софта. так-же и наоборот. а докучи еще бывают ромхакеры, которые совсем не ромхакеры, но делают вполне себе годные ромхаки :)
Б. по плану происходит плавное вкатывание в процесс программирования от простого к сложному, от объяснения к практике.

изначально я хотел делать тему, как когда-то 500 лет назад для особой хитрой программы для игры Lineage 2 :) я на форуме вешал программные квесты, где участники их пытались решать и от простых задач мы потихоньку двигались к сложным. так было бы интереснее, но зная "активность" форума... отказался от этой затеи. решил постить только готовый код с объяснениями, с нарастающей сложностью задачи.

но увы... набежали тролли, развели флуд и информативные посты начинают тонуть :)

Добавлено позже:
ну и что касается слова Бейсик в заголовке. есть такая предвзятость у людей:
 - А, Бейсик? Так мы его в школе проходили. Дерьмо. Читать эту тему не буду.
неверное отношение :) следует понимать не как: Бейсик это что-то забытое и не интересное. А как вполне себе рабочий и действенный инструмент для решения не супер сложных задач в ромхакинге. поэтому то там выше я и написал - кто понял тот понял, а кто не понял... улыбаемся и машем.

15
ты зачем все карты раскрыл? :) ну сидят они - не читали и осуждают и пусть себе сидят :) а ты взял и открыл им глаза на истину вселенной. блин. я расстроен :)

16
Поцоны, расходимся. Нас наебманули! Оказывается Бейсик-барсик не тащит :( И ничего в нем сделать невозможно... тото я думаю почему я ни один свой проект не закончил - оказывается язык не тот! Кто-ж знал? Где вы, атцы-ногебаторы с++, раньше были? Почему не подсказали мне - неучу?

Так... а что значит не можешь? Усё я могу :) Просто не хочу... На самом деле пробно интересовался у продвинутых чуваков на тему создания эмулятора в виде длл, так как переписать полностью под мой язык я конечно-же не смогу. Вот дллку-эмулятор можно было бы свободно подключать к проектам, как это было с дллкой Sharpnull для GEMS - шикарная фигня! Доволен по самые помидоры! Поэтому с лету подключить существующий эмулятор с дебагом для иды не получилось. Типа я вижу что Ида и эмуль стартуют свои веб сервера, потом ида чото шлет эмулю... но повторить не получилось :) А с какой-то там шаред мемори... я хз чо это и откуда береться. Надо с буржуями советоваться, чтоб подсказали как этот эмуль с дебагом подключить. Правда там еще надо знать что от него хотеть - тут я тож пока не бум бум как получать от него адреса, которые эмуль найдет в процессе игры. Но одну часть марлезонского балета я все-таки сделал. Хотелось бы конечно лучше, так как кнопка разбора кода С работает у меня медленновато и это дико дико раздражает :) Задача была читать ром, разбирать код, и создавать асм файл, который без танцев с бубном, какие необходимы кое какому другому проекту, могли бы собираться православным ASM68K.exe. До флешроялю только не хватило эмуль с дебагом из идовского проекта. Ну и ассемблер свой тоже будет сделать тяжело, так как регулярок для разбора команд там надо будет даже не миллион - а миллиард :) Если стандартные я еще смогу добавить, то вот варианты с формулами, где всякие арифметические действия нужны внутри одного параметра - тут полный швах. Хотя надо опять таки поинтересоваться у братьев-буржуев - подозреваю там давно есть нужное решение и нужно только его спионерить.

пример, правда я вот не помню распоследнее ли там залито или нет: https://www.emu-land.net/forum/index.php?action=dlattach;topic=88184.0;attach=273451


Так... опять пришли всякие флудеры, нафлудили пол страницы, отвлекли меня-любимого, от проекта по захватыванию вселенной. Сейчас наша супер мега программа читает и пишет long - 4 байтовые значения. Полагаю пришло время добавить некоторую универсальность и сделать переключатель 4-2-1 байт. Для этого нам понадобятся новые приблуды:
OptionGadget и SetGadgetState.
SetGadgetState - команда для отметки. То есть в ней мы указываем какой гаджет нужно обработать и вторым параметром 1 или 0 - произвести отметку или убрать отметку с гаджета.
OptionGadget - это круглишок с текстом, который надо отметить мышкой. Когда выбирается какой-то пункт, то со старого отмеченного пункта точка отметки убирается, то есть происходит переключение. Чтобы система понимала, что эти 3 пункта это 3 варианта для одного какого-то случая - надо их размещать рядом:
  OptionGadget(#LongTipParametra, 10, 140, 100, 20, "long (4 байта)")
  OptionGadget(#WordTipParametra, 120, 140, 100, 20, "word (2 байта)")
  OptionGadget(#ByteTipParametra, 240, 140, 100, 20, "byte (1 байта)")
  SetGadgetState(#LongTipParametra, 1)

Неправильный вариант:
  OptionGadget(#LongTipParametra, 10, 140, 100, 20, "long (4 байта)")
  SetGadgetState(#LongTipParametra, 1)
  OptionGadget(#WordTipParametra, 120, 140, 100, 20, "word (2 байта)")
  OptionGadget(#ByteTipParametra, 240, 140, 100, 20, "byte (1 байта)")

Система будет считать это как два разных переключателя - одновариантный сверху и с двумя вариантами на выбор снизу. Чото я как-то замудрено написал :)))

И наверное надо еще прояснить момент с Select. Чтобы не плодить миллион IF - Если - существует такая удобная фигня Select.
Если А = 1, то сделать это...
Как бы понятная конструкция. Проверяем какую-то переменную на соответствие её единице. Но что если у этой переменной может быть много значений, и в зависимости от числа нужно делать разные действия? Можно конечно сделать миллион IF:
If A = 1
ElseIf A = 2
ElseIf A = 3
ElseIf A = 4
...
Не совсем удобно. Select докучи еще и может задавать целый сегмент значений:
Select A
  Case 1
  Case 2 to 4 ; с двух до четырех
  Case 5 to 10, 14, 45 ; с пяти до десяти, и отдельно 14 и отдельно 45
  Default ; все что не вошло в предыдущие пункты. 0, 11, 12, 13, с 15 до 44, с 46 и до бесконечности
Endselect


Итак, с добавленными этими вариантами размера переменной код будет выглядеть так:


; перечисление окон, гаджетов и файлов
Enumeration
  #Window
 
  #TextPutDoRoma
  #StrokaPutDoRoma
  #KnopkaPutDoRoma
 
  #TextAdresParametra
  #TextAdrerParametra2 ; значок доллара будет
  #StrokaAdresParametra
 
  #TextZnachenieParametra
  #TextZnachenieParametra2 ; $ 
  #StrokaZnachenieParametra
  #ProchitatZnachenieParametra
 
  #TextNovoeZnachenie
  #TextNovoeZnachenie2 ; $ 
  #StrokaNovoeZnachenie
  #ZadatNovoeZnachenie 
 
  #TextTipParametra
  #LongTipParametra
  #WordTipParametra
  #ByteTipParametra
 
  #File
EndEnumeration


; создание окна
If OpenWindow(#Window, 100, 100, 410, 200, "Я у мамы программист.", #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)
 
  ; строчка текста тип параметра
  TextGadget(#TextTipParametra, 10, 120, 70, 20, "тип:")
 
 
  OptionGadget(#LongTipParametra, 10, 140, 100, 20, "long (4 байта)")
  OptionGadget(#WordTipParametra, 120, 140, 100, 20, "word (2 байта)")
  OptionGadget(#ByteTipParametra, 240, 140, 100, 20, "byte (1 байта)")
  SetGadgetState(#LongTipParametra, 1)
  TipPeremennoy = 4
 
  ; кнопка прочитать
  ButtonGadget(#ProchitatZnachenieParametra, 80, 170, 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
                     
                     ; в зависимости от выбранного типа переменной будем читать различное количество байт
                     Select TipPeremennoy
                       Case 4 ; long
                         ; читаем 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))
                         
                       Case 2 ; word
                         ; читаем 2 байта из памяти по одному байту и сдвигаем итоговое значение
                         Result = PeekA(mem) << 8
                         Result | PeekA(mem + 1)
                         
                         ; выводим в окошке на теле программы итоговое значение с переводом в хекс
                         SetGadgetText(#StrokaZnachenieParametra, Hex(Result, #PB_Word))
                       Case 1 ; byte
                         ; просто читаем 1 байт с нужного адреса
                         Result = PeekA(mem)
                         
                         ; выводим в окошке на теле программы итоговое значение с переводом в хекс
                         SetGadgetText(#StrokaZnachenieParametra, Hex(Result, #PB_Byte))
                     EndSelect
                   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$)
                     
                     ; в зависимости от типа переменной запись будет происходить по разному
                     Select TipPeremennoy
                       Case 4 ; long
                         ; разбиваем значение на 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)
                       Case 2 ; word
                         ; разбиваем значение на 2 байта
                         first.a = Zapis >> 8
                         secnd.a = Zapis
                         PokeA(mem, first)
                         PokeA(mem + 1, secnd)
                       Case 1 ; byte
                         PokeA(mem, Zapis)
                     EndSelect
                         
                     ; вроде как в памяти у нас уже сидит новое значение.
                     ; значит надо перезаписать весь файл целиком
                     ; читаем путь до рома из окошка в программе
                     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
             ;}
             
           Case #LongTipParametra
             ; переключим флаг, сигнализирующий какая переменная нам нужна
             TipPeremennoy = 4
             ; стираем текст из окошка значения адреса
             SetGadgetText(#StrokaZnachenieParametra, "")
             
           Case #WordTipParametra
             TipPeremennoy = 2
             ; стираем текст из окошка значения адреса
             SetGadgetText(#StrokaZnachenieParametra, "")
             
           Case #ByteTipParametra
             TipPeremennoy = 1
             ; стираем текст из окошка значения адреса
             SetGadgetText(#StrokaZnachenieParametra, "")
             
         EndSelect
         
       ; событие касается закрытия окна
       Case #PB_Event_CloseWindow
         qiut = 1
   
     EndSelect
     
     ; цикл крутится, пока не сработает закрытие окна и соответственно qiut станет равен 1
   Until qiut = 1
   

EndIf


; завершение программы
End

17
да я тоже не понимаю зачем ты делаешь свой плагин к иде. но ты ведь его делаешь :) значит оно кому-то нужно. в смысле весь этот бред с костылями - ида, питон, плагин, эмулятор... бред собачий :) делай сразу единую ОДНУ прогу, без костылей и ошибок. сразу чтоб в одном флаконе было и эмулятор, и весь разбор, и нормальное сохранение, и сразу компилер, и чтоб все происходило в памяти, без создания временных файлов на диске. (главное конечно чтоб ошибок в коде не рожало. типа как сейчас прозевал разницу .s и .w кажись... где-то там... забыл где. jsr наверное. где джамп сам на себя какой-то корявый. и еще что-то там, уже всё и не упомню...)


Отвлеклись. Продолжаем упарываться. То что уже сделано, то есть общая конструкция - можно сказать это уже готовый патчер для рома. Типа нам что-то известно - адреса и значения, и этим проектом можно что-то там исправить по известным адресам, втуливая туда нужные исправленные значения. Однако продолжая тему 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

18
дааааа кто понял - тот понял, а кто не понял - тот Филлип Киркоров. ой. то есть тот Yoti.

Продолжаем упарываться. У нас есть отдаленное понимание как построить окно программы и как худо бедно читать и писать в файл. Пора совместить. Для начала думаю можно сделать так, что при запуске программы - она будет читать файл нашего "рома", показывать оригинальное значение $12345678 и будет иметь кнопку, жмакая которую мы будем производить вписывание этого самого числа куда нужно.

; перечисление окон, гаджетов и файлов
Enumeration
  #Window
 
  #TextPrivetMir
  #TextHex
  #KnopkaSave
 
  #File
EndEnumeration

; читать файл можно до создания окна.
; а докучи если файл не смог быть прочитан - окно можно даже не создавать
; а сразу написать, что шеф, все пропало!

; ReadFile - читаем, то есть файл недоступен для записи, а только читаем для безопасности.
If ReadFile(#File, "D:\SEGA\Forum\testfile.bin")
 
  ; прыгаем на известный нам адрес $1C внутри файла
  FileSeek(#File, $1C)
 
  ; читаем в нужном виде нашу переменную long размером 4 байта
  x = ReadAsciiCharacter(#File)
  x << 8
  x + ReadAsciiCharacter(#File)
  x << 8
  x + ReadAsciiCharacter(#File)
  x << 8
  x + ReadAsciiCharacter(#File)
 
  ; закрываем файл
  CloseFile(#File)
 
Else
  ; условите "Иначе"
  ; то есть если программа не смогла найти или не смогла открыть наш файл рома
  ; выходит случилась ошибка
  ; выводим сообщение об ошибке и закрываем программу
  MessageRequester("Алярм!!!111расрас", "Ошибка! Я вся такая не смогла прочитать файл. Или не нашла его, или он открыт в другой программе. Черт знает что там случилось.")
  ; прыгаем в конец программы, чтобы не открывать окно.
  ; окно получается бесполезно - мы же не смогли прочитать оригинальный файл
  Goto marker
 
EndIf


; создание окна
If OpenWindow(#Window, 100, 100, 300, 100, "Я у мамы программист.", #PB_Window_MinimizeGadget | #PB_Window_ScreenCentered)
 
  ; рисуем гаджеты на окне
 
  ; в х у нас сидит число. а числа текстовой гаджет показывать не умеет.
  ; ему надо перевести число в текст. это делается с помощью Str(x)
  ; текстовая переменная должна иметь значок доллара в конце
  ; типа x$. то есть х это число, а x$ это текст.
  x$ = Str(x) ; но это в десятичном виде. а нам надо в хекс
  TextGadget(#TextPrivetMir, 10, 10, 200, 20, "Оригинальное число: " + x$)
 
  x$ = Hex(x, #PB_Long) ; вот теперь в тексте x$ сидит число в виде хекс
  TextGadget(#TextHex, 10, 30, 200, 20, "В хекс виде: $" + x$)
 
 
  ButtonGadget(#KnopkaSave, 10, 60, 50, 20, "Вписать")
 
  ; бесконечный цикл обработки событий окна
  Repeat
   
     ; определяем с чем именно мы имеем дело
     Select WaitWindowEvent()
         
       ; событие касается какого-то гаджета 
       Case #PB_Event_Gadget
         
         ; раз событие касается гаджетов, то надо перебрать какой именно гаджет имеет это событие
         Select EventGadget()
             
           Case #KnopkaSave ; оказывается гаджет кнопки
             If EventType() = #PB_EventType_LeftClick
               ; если событие клик левой кнопкой мышки
               
               ; тут мы открываем файл для записи. если что-то пойдет не так - можно испортить файл.
               If OpenFile(#File, "D:\SEGA\Forum\testfile.bin")
                 
                 ; прыгаем в нужное для записи место
                 FileSeek(#File, $3C)
                 
                 ; в х все еще сидит нужное нам число
                 ; производим специальную операцию записи в нужном нам виде
                 y = x >> 24
                 WriteAsciiCharacter(#File, y)
                 y = x >> 16
                 WriteAsciiCharacter(#File, y)
                 y = x >> 8
                 WriteAsciiCharacter(#File, y) 
                 WriteAsciiCharacter(#File, x)
                 
                 ; закрываем файл
                 CloseFile(#File)
               Else
                 ; если файл мы не смогли открыть - надо сообщить об ошибке
                 MessageRequester("опять ошипка :(", "Сохранение файла не удалось. Или он открыт в другой программе, или еще какая катавасия случилась.")
               EndIf
               
             EndIf

         EndSelect
         
       ; событие касается закрытия окна
       Case #PB_Event_CloseWindow
         qiut = 1
   
     EndSelect
     
     ; цикл крутится, пока не сработает закрытие окна и соответственно qiut станет равен 1
   Until qiut = 1
   

EndIf

; метка для досрочного завершения программы, когда файла рома не нашлось
marker:

; завершение программы
End

19
Работа с файлами.

Для нас цифры это цифры. Типа написал ручкой на бумажке 500 и понятно что это 500. В случае записи в файл - тут не все так однозначно :) Там есть такое понятие как тип переменной - byte, word, long. В чем разница? Дело в размере, который будет занят подобным числом. Если нам надо записать число 13 - достаточно будет byte. А если 1300 - то байта уже не хватит. Понадобится word. А если нам надо записать какоенить 128 000 то даже word не хватит - и нужен будет long. Размеры сколько байт они занимают и какие у них лимиты чисел откуда и докуда - можно посмотреть в таблице. нас интересует верхняя часть, красным.



Тип переменной и её размер мы вроде как уяснили. Теперь обсудим отображение этих чисел для глаз. С точки зрения компьютера то нет никакой разницы, а вот нам - человекам - надо кое что уточнить... В основном их используется 3 типа:
обычные десятичные, как у нас, у людей: 8, 9, 10, 11, 12...
число в хексе, обычно так выглядит в хекс редакторе: 8, 9, A, B, C (чтобы уточнить, что это именно хекс, то обычно пишут значок бакса, и потом уже число $8, $9, $A, $B, $C... либо, если аллергия на валюту: 0х08, 0х09, 0х0A, 0x0B, 0x0C)
бинарный код, то есть нули и единички: 00001000, 00001001, 00001010, 00001011, 00001100 (чтобы уточнить что это в бинарном виде, то обычно пишут значок процента и потом уже число %00001000, %00001001, %00001010)

Но и это еще не все. Запись переменных word и long может быть двух видов. Big-Endian и Little-Endian. Иначе говоря один порядок правильный, второй перевернутый. Какой из них какой - черт его знает :) PB, зараза, может читать и писать только в перевернутом. А обычно в файлах рома порядок правильный. Типа для примера long:
в роме $12, $34, $56, $78
PB прочитает как $78, $56, $34, $12
Эту проблему с невозможностью писать в нужном для нас виде отметим, но пока отложим. Вернемся к ней попозже.


Теперь переходим к практике. Во вложении будет прикреплен тестовой файл. Для понимания процесса хорошо бы чтоб на компьютере был установлен WinHex - чтобы можно было проверять правильность работы нашей программы.



Работа с файлом может происходить двумя способами:
открываем и работаем с файлом прямо на месте, то есть на жестком диске.
открываем, читаем в память, закрываем, работаем в памяти, если надо сохранить, переписываем файл из памяти на жесткий диск.

На первоначальном этапе думаю мы пойдем первым путем.

Нам понадобятся команды открытия и закрытия файлов, чтения, записи и прыжка в нужное место файла. Из скрина выше нам известен адрес, где лежит наша переменная $12345678 - $1C и мы знаем что она long. Значит нам надо открыть файл, прыгнуть на этот адрес $1C и прочитать. Вроде задача не сложная.


Enumeration
  #File 
EndEnumeration

If ReadFile(#File, "D:\SEGA\Forum\testfile.bin")
 
  FileSeek(#File, $1C)
 
  x = ReadLong(#File)
 
  CloseFile(#File)
EndIf


Debug x                ; как десятичное. не интересно.
Debug Hex(x, #PB_Long) ; в виде хекс. то что надо.


И нас ждет эпик фейл :) Так как число прочиталось в перевернутом виде. Вместо $12345678 мы видим $78563412.



Чтобы выкрутится - можно сделать так: читать не как long - то есть 4 байта за раз - ReadLong, а читать 4 раза по одному байту - ReadAsciiCharacter, и просто потом их сдвигать в нужное место. Звучит конечно страшно и не понятно, но тут можно особо не вникать, так как код все сделает. Я всегда так делаю: если код не понятный, но работает - просто принимаешь на веру и радуешься :)


Enumeration
  #File 
EndEnumeration

If ReadFile(#File, "D:\SEGA\Forum\testfile.bin")
 
  FileSeek(#File, $1C)
 
  ;x = ReadLong(#File) ; заккоментировали, и поэтому текст зеленый и не считается
  x = ReadAsciiCharacter(#File)
  x << 8
  x + ReadAsciiCharacter(#File)
  x << 8
  x + ReadAsciiCharacter(#File)
  x << 8
  x + ReadAsciiCharacter(#File)
 
  CloseFile(#File)
EndIf


Debug x                ; как десятичное. не интересно.
Debug Hex(x, #PB_Long) ; в виде хекс. то что надо.


Что касается сдвига, а в данном случае x << 8 тут надо немного уточнить. 1 байт состоит из 8 битов. Эти самые биты можно увидеть отобразив число в бинарном виде. Например:
x = 120 ; это десятичный вид
Debug Bin(x)
покажет как %1111000 - бинарный, или биты (на самом деле %01111000 просто нолики в начале съедает.)
если мы это число "сдвигаем" << 8, то это означает добавляем нолики в конце.
было %01111000 стало %0111100000000000
типа 120 в хекс это: $78
$78 << 8 = $7800

то-же самое если мы сдвигаем в другую сторону. например было число $789A
$789A >> 8 = $78

С этим разобрались. Теперь усложним наш код и изменим ReadFile на OpenFile, так-же прочитаем из нужного места и перевернем наше число, и попытаемся его записать в другое нужное место.


Enumeration
  #File 
EndEnumeration

If OpenFile(#File, "D:\SEGA\Forum\testfile.bin")
 
  FileSeek(#File, $1C)
 
  ;x = ReadLong(#File) ; заккоментировали, и поэтому текст зеленый и не считается
  x = ReadAsciiCharacter(#File)
  x << 8
  x + ReadAsciiCharacter(#File)
  x << 8
  x + ReadAsciiCharacter(#File)
  x << 8
  x + ReadAsciiCharacter(#File)
 
 
  FileSeek(#File, $3C)
 
  WriteLong(#File, x)
 
  CloseFile(#File)
EndIf

Запускаем, смотрим наш файл в хекс редакторе и видим, что там ошибка. Да, мы прыгнули в нужное место записи - $3C, но записывать надо было не стандартными методами PB - WriteLong - 4 байта за раз, а точно так-же разбив long на 4 байта по одной штуке и писать их по очереди. А то получается мы записали число задом наперед :)

Enumeration
  #File 
EndEnumeration

If OpenFile(#File, "D:\SEGA\Forum\testfile.bin")
 
  FileSeek(#File, $1C)
 
  ;x = ReadLong(#File) ; заккоментировали, и поэтому текст зеленый и не считается
  x = ReadAsciiCharacter(#File)
  x << 8
  x + ReadAsciiCharacter(#File)
  x << 8
  x + ReadAsciiCharacter(#File)
  x << 8
  x + ReadAsciiCharacter(#File)
 
 
  FileSeek(#File, $3C)
 
  ;WriteLong(#File, x) ; выкидываем
  y = x >> 24
  WriteAsciiCharacter(#File, y)
  y = x >> 16
  WriteAsciiCharacter(#File, y)
  y = x >> 8
  WriteAsciiCharacter(#File, y) 
  WriteAsciiCharacter(#File, x)

 
  CloseFile(#File)
EndIf

Здесь как раз используется сдвиг в другую сторону x >> 8. И глядя на код вы можете сказать:
- Но, позвольте! x >> 24 я согласен, что превратит $12345678 в $12, но ведь x >> 16 сделает не нужные нам $34, а $1234 - а это совершенно не правильно!
Хорошее замечание. Придется ответить:
- А тут все дело в команде записи - WriteAsciiCharacter - она записывает только 1 байт. Поэтому независимо от того, что в нашем числе сейчас сидит двухбайтовое $1234 - запишутся в файл только последние $34 - то что нам и надо.

20
Тема создана с целью наиболее простым языком описать процесс создания своего какого-то инструмента для редактирования чего-либо в игре. Это не ромхакинг. Это предполагается, что ромхакер уже знает что где лежит в роме и в каком формате. И это что-то существующими доступными программами не редактируется, а хотелось бы сделать что-то свое, однокликовое, с блекджеком и легкодоступными женщинами.

Будет для статьи использоваться не совсем удобное и знаменитое, а докучи еще и платное - PureBasic. Ну и, понятно, Windows. Как бы он тоже платный... главное что некоему Билли Г. что автору PB не рассказывать о существовании торрентов.

Как понятно из названия - это Basic, только что с плюшками современными. Но в общем и целом те-же яйца, только в профиль. И нас интересует портабельная версия, чтобы ничего не устанавливать.




Дальше теория. Код обычно выполняется сверху вниз построчно. Типа пробежался сверху вниз, закончился, и получились какие-то результаты. Однако мы хотим создать окно "Привет Мир!" и чтобы оно висело по середине экрана и радовало пользователя своим висением. Следовательно код должен будет бежать сверху вниз, дойти до момента создания окна, создания элементов внутри окна - то есть надписи "Привет Мир!", и после этого дальше начать по кругу гонять бесконечный цикл "ожидание событий" - без этого цикла окно мелькнет и исчезнет, а нам нужно продолжительное висение, пока пользователь не нажмет "закрыть окно".

В этом самом бесконечном цикле есть разбор событий и нам нужно будет добавить событие "закрыть окно". Этот разбор разбирает какое это окно, где событие случилось, какой элемент внутри окна имеет событие, какое именно событие происходит - движение мышкой или может быть клик левой кнопки, или может правой. То есть получается у самого окна, у всех его элементов, у событий - есть свои уникальные номера, чтобы программа понимала что с ней творится. Можно прям так и объявлять: окно 0, кнопка 1, картинка 2... Как бы все будет работать, но визуально для глаз удобнее все-же давать "человеческие" названия окнам и кнопкам. типа окно #Window, кнопка #Button или если это скажем кнопка сохранения #ButtonSave... или даже #KnopkaSave чтобы вообще все было понятно. Эти названия друг за дружкой можно перечислять в верхней части кода, в разделе Enumeration. Визуально мы - человеки - будем понимать где какая кнопка, а с точки зрения программы это будут те-же самые цифры 0, 1, 2, 3...

Дальше элементы внутри окна. Они называются гаджеты. Их там небольшой список и они различаются по внешнему виду и функционалу. Типа скажем текстовая строчка, или кнопка, или картинка, или ниспадающий список, или гаджет с отметкой галки (CheckBoxGadget)... У каждого свои задачи и свой набор событий. Типа кнопка может получить и обработать нажатие левой кнопки мышки (правой к сожалению нет, хотя порой очень надо). И так далее.




По итогу код нашей программы для взлома пынтагона выглядит так:

Enumeration
  #Window
 
  #TextPrivetMir
  #KnopkaOK
EndEnumeration


If OpenWindow(#Window, 100, 100, 300, 100, "Я у мамы программист.", #PB_Window_MinimizeGadget | #PB_Window_ScreenCentered)
 
  TextGadget(#TextPrivetMir, 10, 10, 100, 20, "Привет Мир!")
 
  ButtonGadget(#KnopkaOK, 10, 50, 50, 20, "OK")
 
  Repeat
     Select WaitWindowEvent()

       Case #PB_Event_Gadget

         Select EventGadget()
           
           Case #KnopkaOK
             If EventType() = #PB_EventType_LeftClick
               Debug "была нажата кнопка ОК"
             EndIf

         EndSelect

       Case #PB_Event_CloseWindow
         qiut = 1
   
     EndSelect
   Until qiut = 1

EndIf

End

То есть сначала наверху блок Enumeration, где перечислены окна и кнопки, которые мы будем использовать. Потом создание окна программы OpenWindow - где внутри указаны начальные координаты окна, ширина и высота, и заголовок в кавычках. Специальные параметры в конце в количестве два штуки говорят программе, что надо рисовать наше окно в центре экрана и чтобы была кнопка "свернуть программу", ну и крестик "закрыть программу".

После создания окна идет рисование гаджетов - в данном случае текстового TextGadget и кнопки ButtonGadget. У них так-же как и у окна есть стартовые координаты, но теперь уже не экрана - а координаты на самом окне, высота и ширина, и надписи в кавычках.

Потом начинается тот самый бесконечный цикл - Repeat и он крутится до строчки Until qiut = 1, то есть пока переменная qiut - выход, или закрытие программы, не будет ровняться единичке. Внутри этого цикла происходит разбор где произошло событие - то есть в данном случае перечислены два варианта: тело самой программы (типа пользователь нажал кнопку "закрыть") и какой-то гаджет (в данном случае кнопка).

Что касается Debug - это просто вывод текста в специальное окошко. Оно нам нужно будет удостоверится, что событие - клик по кнопке ОК - действительно обрабатывается.

На каждую команду типа OpenWindow или TextGadget можно будет в PureBasic навести мышкой, кликнуть и нажать F1 - вылезет подсказка с полным разбором где какой параметр за что отвечает. И даже примеры кода, которые можно будет скопировать в новое окошко и позапускать - посмотреть результаты.

И да - везде где не попадя стоят If - If OpenWindow, If EventType()... PB это вообще очень не уверенный в себе язык программирования и поэтому лучше на всякий случай добавлять эти самые условия - If - Если. Если открылось окно, то... Если событие было кликом мышки, то... OpenWindow в принципе будет работать и без If, но мало ли в каких-либо случаях Windows не даст создать окно, а программа то захочет выполнятся дальше и дальше по коду идет рисование текстового гаджета - а где она его нарисует, если окно не создалось? Программа вылетит с ошибкой. А так у нас стоит If - окно не открылось? Ничего страшного. Просто завершаем работу, как будто программа не стартовала вовсе. Ну или можно вывесить сообщение, что дескать по какой-то причине мы не смогли создать окно, а следовательно кина не будет.


Пока, надеюсь, все понятно :)

п.с.: пошел на кухню и понял что понятно не всё. надо скопировать текст программы, вставить в PureBasic и чтобы не елозить по менюшкам - нажать F5.

Добавлено позже:
а как тут начать новый пост, чтобы он был новым постом, а не *добавлено позже ?

21
надо тему стартануть :) чтоб с описанием, как для детей с тремя классами церковно-приходской школы с айтишным уклоном, как у меня, как создавать простые редакторы по потребностям для ромхакинга.

22
так ты главное начни :) а там само собой пойдет. я вот тоже не силен... я больше идеи и чужой код в одну кучу собираю и худо бедно заставляю их вместе работать :) когданить книгу напишу как я с приключениями с мира по нитке собирал свой проект :)

23
сделай себе что-то типа самодельного gif редактора и там ваяй движущиеся картинки. это не сложно. сразу чтоб вставляло 24битную картинку налету, с произведением пастеризации и созданием палитры и чтоб сразу все совместимо было.

24
прислали тут ссыль :) неудержался чтоб копирнуть сюда :)))
https://duaneandbrando.bandcamp.com/track/battletoads-2

25
дело в том, что чтобы рисовать рамку - надо рисовать эту самую рамку. а памяти совсем нет свободной для тайлов полосок рамки. отчасти можно конечно тайлы курсора использовать, но за чей счет? рисовать спрайтами? или рисовать на втором слое? не говоря уже про оперативную память, которая под завязку. на самом деле если бы была там такая свободная вещь, то первое что нужно сделать это не рамка и селект кучи юнитов - а просчет путей, чтобы юниты не застревали или не ездили длинными путями, когда есть путь короче.

26
на тот момент дефлемаск еще был в тренде :) потом Делек объявил что переходит на платную основу и все начало скатываться. потом стартанул Furnace и весь народ туда ломанулся, так как там блек джек обещали с женщинами и еще и бесплатно.

27
да это просто люди не понимают объема блудняка, в который надо вписаться, чтобы все это сделать :) я уже пол года искаю кто бы музыку сделал, не говоря уже о художниках, и прочих высокоодухотворенных персон, которые нужны и которые нужны хорошие, и чтоб еще денег не требовали до кучи. так даже конвертер из миди в дефлемаск сделал и там прям на теле программы написал: что дескать ищу кто может сделать каверы Игры Престолов... типа вот специально для вас музыкантов конвертер, пользуйтесь, и может быть кто-то захочет сделать каверы. и чего? нуль эмоций :)))) а уже лет 5 наверное прошло как он по сетям гуляет... даже архив с миди файлами там в теле программы приложен и краткое техническое описание мелодий, что нужны чтобы их заюзать в ромхаке. высокодуховные и высоколенивые, заразы :) посему то оптимизма в этом неблагодарном деле быть не может. ведь чтобы реально сделать полноценный ромхак, а считай Джим Червяк 3, то нужна бригада разнорабочих, где каждый разбирается в своем деле. а не так, чтобы и жрец и пьец и на дуде игрец в одном лице.

28
I predict his next post:
Цитата
I accidentally blew up my console. help me fix it.

29
если шрифт сдвинуть во второй сегмент памяти, то лимит может быть увеличен и картинка может быть более детальной. может и в третий можно, но там кажись места не хватит чтоб все символы шрифта влезли.

30
Не дружу с тобой больше
блииин... как не удобно получилось...

поцоны, знаете, МК3 такая классная игра!

Добавлено позже:
И пару батников - один для сборки гемсовых потрохов, второй для сборки рома
неповеришь, но в Дюне это примерно плюс минус так и сделано :)))) то есть если ты на экране редактирования музыки - и жмешь собрать ром - собираются гемс банки. если ты на любом другом экране, скажем редактирование заводов - то при сборе рома - гемс банки не пересобираются.

правда если ты был на экране музыки, изменил что-то, переключился на другой экран и собираешь ром... тогда изменения не попадут в ром. или я это тоже продумал? блин. не помню :) должен был. но далеко не факт что все-таки сделал этот флаг проверки какого-либо изменения в банках.

Добавлено позже:
Тоже мне... Сравнил файтинг с платформером.
и это не я сравнил! :) это в ответ на реплику собеседника :) вот он сравнил, он! все шишки ему!

Страницы: [1] 2 3 4 5 ... 79 Далее