Автор Тема: Программирование редакторов для внесения каких-то изменений в ром.  (Прочитано 2067 раз)

0 Пользователей и 1 Гость просматривают эту тему.

Оффлайн SeregaZ

  • Пользователь
  • Сообщений: 2538
  • Пол: Мужской
  • ливнул с форума
    • Youtube
    • Просмотр профиля
универсальную прогу... надо программировать  :lol:

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

Оффлайн SeregaZ

  • Пользователь
  • Сообщений: 2538
  • Пол: Мужской
  • ливнул с форума
    • Youtube
    • Просмотр профиля
Экраны.

Часть первая, теоретическая, всем известная. можно сразу переходить ко второй - практической части.

Если со спрайтами все понятно - мелкие фрагменты от 1х1 тайла до полноприводных 4х4, то с экранами маленько сложнее. Эти самые экраны состоят из трех частей, ладно, четырех:
1. палитра
2. тайловый сет
3. тайловая карта
4. режимы работы

С палитрой и отчасти с тайловым сетом мы встречались еще во времена спрайтов и там как бы все понятно, но что еще за тайловая карта? Это по сути матрица, с инструкциями как на ней расположены тайлы друг за другом. Там каждому тайлу на изображении дополнительно указываются его параметры:
номер тайла из тайлового сета
приоритет
палитра
зеркало по Х
зеркало по У

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


Конечно на данном этапе возникнет вопрос: да нахрена это надо? просто загрузил PNG картинку и пользуйся! С точки зрения современного пользователя, насмотревшегося всяких 4к на ютубах это прям очень даже не понятно, но вот в те времена, когда трава была зеленее - железо только только начинало развиваться и поэтому тогдашние инженеры выкручивались как могли чтобы сделать и подешевле и чтоб возможности были вполне себе конкурентными, чтобы нинденду обниндендить. Вот и пришли они к тому, что приставка будет иметь 4 палитры - то есть 2 бита из тех 16 бит в тайловой карте (00, 01, 10, 11 - 0, 1, 2, 3 номера палитр) и 11 бит под тайловый номер (а 11 бит это %111 1111 1111 или $7FF в хекс или 2047 в десятичных - 2047 возможных тайла)

В принципе такая куча тайлов не так уж и мала и есть куда разгуляться... НО! под видеопамять у нас выделено всего 65 килобайт в приставке. Плюс еще помимо самих тайлов в этой-же памяти должны болтаться две тайловые карты под два слоя картинки и еще должна болтаться таблица спрайтов. Получается полноценно втулить изображение может нехватить памяти - ведь под каждый тайл надо по 32 байта (напомню: в одном байте 2 пикселя и поэтому 8 строк помножить на 8 столбиков = не 64, а 32). Вот с точки зрения спрайтов, где тайлы должны идти друг за другом - все плохо. А вот с точки зрения экранов - маленько получше, так как тут инженеры припасли трюки ушами. Во первых - кто сказал, что нумерация тайлов обязательно должна быть 1 тайл, 2 тайл, 3 тайл, 4 тайл, 5...... ? Можно оптимизировать изображение. Совсем необязательно чтобы у картинки были все тайлы уникальные. Можно взять и сделать сравнение тайлов и если 1 и 2 тайл одинаковые, то значит можно в тайловой карте для второго тайла указать то, что он будет использовать 1 номер тайла и картинка будет точно такая-же как и была, то есть ничего не потеряет. Тоже самое касается каждого тайла относительно всех остальных тайлов на изображении - надо прогнать по всей картинке и выкинуть повторяющиеся тайлы. Иными словами - эти тайловые карты позволяют нам сжимать изображение. В результате тайловый сет может занимать меньше памяти, чем ежели бы он был без этого "сжатия" напрямую. Второй трюк заключается в использовании зеркал, что может сжать тайловый сет еще больше, то есть тайл может быть перевернут на изображении - скажем по вертикали, или даже и по вертикали и по горизонтали.

Вот на флаге моей страны - Казахстана есть повторяющиеся элементы. Если в первом случае все тайлы уникальные это займет 32 тайла, а во втором повторяющиеся будут отсечены - их станет 14. А если еще проверить тайлы на зеркала, а в данном случае по вертикали - то можно еще 2 тайла отсечь и по итогу тайлов станет 12. Вот в этом суть использования тайловых карт.


С палитрой тоже все понятно - каждому тайлу в тайловой карте можно указать какую именно палитру он будет использовать.

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

Что касается режимов работы экрана, то это касается размеров экранов в зависимости PAL и NTSC, а так-же выбранного режима - H40 или H32. Тут позволю себе копирнуть из интернетов:
NTSC:
Режим H40 — 320x224 пикселя (40x28 тайлов). Самый распространённый режим разрешения
Режим H32 — 256x224 пикселей (32x28 тайлов). Менее популярный режим разрешения
PAL:
Режим H40 — 320x240 пикселей (40x30 тайлов). Самый распространённый режим разрешения
Режим H32 — 256x240 пикселей (32x30 тайлов). Менее популярный режим разрешения
« Последнее редактирование: 26 Октябрь 2024, 19:48:19 от SeregaZ »

Оффлайн SeregaZ

  • Пользователь
  • Сообщений: 2538
  • Пол: Мужской
  • ливнул с форума
    • Youtube
    • Просмотр профиля
Часть вторая, практическая.

Несмотря что популярный размер это NTSC H40 это 320x224 пикселя - тайловая карта (обычно) бОльшего размера - 512х224, просто правой части экрана не видно. При условии конечно что это статичный экран, а не арена MK3, где можно ходить блудить туда сюда. И поэтому обычно тайловая карта экрана имеет больший размер в роме, но могут быть частные случаи типа как ZT - там тайловая карта ровно 40 тайлов в ширину. Поэтому там при загрузке нужны дополнительные танцы с бубном, со вписыванием такого 40 тайлов ширины экрана в 64 ширинный экран, с невидимой справа частью... но не суть. Мы пока про обычные, чаще встречаемые случаи. Поэтому размер экрана я сделал не 320, а 512 пикселей, чтобы тайловая карта рисовалась вся как есть. Так-же для первого, так сказать, опыта работы с экранами - тайловая карта не использует зеркала. Код с зеркалами мы добавим позже.

Что нового, что ранее не встречалось:
Битовые операции. Для того, чтобы вычленить из 2 байтового значения нужные нам параметры можно использовать сдвиг и маску. То есть к примеру было у нас значение и нам в нем нужна палитра:
%1010111110101111
Пришлось бы сдвигать значение вправо, а после накладывать маску %11. Как бы действия то все понятны... но не совсем удобно. Вот тут то и появляются эти самые битовые операции:
;{ битовые операции
Macro SetBit(Var, Bit)   ; Установка бита.
  Var | (Bit)
EndMacro
Macro ClearBit(Var, Bit) ; Обнуление бита.
  Var & (~(Bit))
EndMacro
Macro TestBit(Var, Bit)  ; Проверить бит
  Bool(Var & (Bit))
EndMacro
Macro NumToBit(Num)      ; Позиция бита по его номеру. ; для вызова сет бит и клер бит.
  (1<<(Num))
EndMacro
Macro GetBits(Var, StartPos, EndPos) ; Прочитать от и до
  ((Var>>(StartPos))&(NumToBit((EndPos)-(StartPos)+1)-1))
EndMacro
;}

Опять таки в чем разница между процедурой и макросом - да черт её знает... главное что работает :) Теперь вместо сдвига и маски будем писать так:
palnum    = GetBits(tmapvalue, 13, 14)То есть берется значение tmapvalue и из него выковыривается значение из двух бит, начиная с 13 по 14 бит - то самое место, где лежит палитра. Точно по такой-же системе получаем номер нужного тайла в тайловом сете:
%1010111110101111
tnum      = GetBits(tmapvalue, 0, 10)То есть значение с 10 по 0 бит.

А зеркала и приоритет будем возвращать путем TestBit
%1010111110101111
vertical  = TestBit(tmapvalue, NumToBit(12))То есть вертикальное зеркало у нас лежит в 12 бите.

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



« Последнее редактирование: 26 Октябрь 2024, 19:50:16 от SeregaZ »

Оффлайн SeregaZ

  • Пользователь
  • Сообщений: 2538
  • Пол: Мужской
  • ливнул с форума
    • Youtube
    • Просмотр профиля
тут это... затык  :lol:

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

там что-то типа:
lea pic_map1,a0
move.l #$40000003,(a2) ; VDP PORT ADDRESS VRAM $C000 - MAP1
move.w #$0001,d3
bsr.s load_map_40x28


load_map_40x28: ; map 40x28 -> 64x28 conversion
moveq #27,d2
moveq #0,d4
@copy_n
moveq #63,d1 ; 39
@copy_l
move.w (a0)+,d0
add.w d3,d0
move.w d0,(a1)
dbf d1,@copy_l
moveq #0,d1 ; 23
@fill_l:
move.w d3,(a1)
dbf d1,@fill_l
dbf d2,@copy_n
rts

полагаю что-то вместо load_map_40x28 надо вписать, чтобы оно pic_map1 (64х28 карта) втулило в нужный кусочек памяти - там где тайловая карта должна болтаться.

у меня не выходит :(

Онлайн Sharpnull

  • Пользователь
  • Сообщений: 5137
    • Просмотр профиля
соответственно в процессе работы конвертирует 40х28 в 64х28. в моем же случае карты уже 64х28. как их втулить то?
Попробуйте так:
load_map_64x28:
moveq #27,d2
@copy_n
moveq #63,d1
@copy_l
move.w (a0)+,d0
add.w d3,d0
move.w d0,(a1)
dbf d1,@copy_l
dbf d2,@copy_n
rts
Т. е. я убрал цикл @fill_l, который добавлял тайлы для заполнения (d3, он же базовый для остальных).
UPD: Или короче:
load_map_64x28:
move.l #64*28-1,d1
@copy_n
move.w (a0)+,d0
add.w d3,d0
move.w d0,(a1)
dbf d1,@copy_n
rts
UPD2: вместо move.l #64*28-1,d1 можно move.w #64*28-1,d1, потому что в dbf только 16 бит учитываются от регистра.
« Последнее редактирование: 05 Ноябрь 2024, 22:15:34 от Sharpnull »

Оффлайн SeregaZ

  • Пользователь
  • Сообщений: 2538
  • Пол: Мужской
  • ливнул с форума
    • Youtube
    • Просмотр профиля
заработало, спасибо :)


Оффлайн SeregaZ

  • Пользователь
  • Сообщений: 2538
  • Пол: Мужской
  • ливнул с форума
    • Youtube
    • Просмотр профиля
Было бы неплохо маленько добавить визуальное разделение на экране. Типа сделать вертикальную зеленую полоску границы экрана на 320 пикселях и добавить сетку тайлов 8х8 пикселей. Полупрозрачных причем.

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

Рисовать будем классической бейсиковской командой Line. Там не сложно сначала стартовые координаты точки откуда линия рисуется, дальше относительные координаты в какую сторону от старта двигаться. А чтобы сделать линию полупрозрачной - надо вместо RGB указывать RGBA, где четвертый параметр как раз таки прозрачность. Так-же режим рисования надо будет переключить на DrawingMode(#PB_2DDrawing_AlphaBlend)

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

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

Ну и раз пошла такая пьянка, то хорошо бы сразу сделать кнопку со сборкой рома через ASM68K.exe, и потом запуск эмуля, где можно было бы поглядеть как оно будет выглядеть в итоге. Для чего понадобится RunProgram. Ну и для переменной EmulatorPath$ надо будет указать свой путь до эмулятора, иначе волшебства не произойдет.

Ну и может быть такой вариант, что сборка рома ASM68K.exe зафейлится и тогда мы запиливаем диалог ошибки с возможностью выбора запуска бат файла отдельно. Дело в том, что при запуске ASM68K.exe окошко мелькает и сразу-же закрывается, не давая прочитать любовные письма, которые он там пишет. Как бы PB имеет функции чтения чужого консольного окна, но затык в том, что ASM68K.exe не дает прочитать, скатина. Гранаты там не той системы и соответственно лог работы ассемблера не дается. С большинством же других консольных программ нет никаких проблем.

Что касается самих зеркал, то там используются те-же самые циклы, как при рисовании обычного тайла без зеркал. Просто мы меняем значения от и докуда крутить местами и в самом цикле указываем направление Step -1, то есть от большего к меньшему. Ну и в случае с Х зеркалом x - 1 для второго пикселя, вместо х + 1 как было при обычном тайле без зеркал.




В конечном итоге дизайн окна теперь выглядит так:

« Последнее редактирование: 06 Ноябрь 2024, 18:44:43 от SeregaZ »

Оффлайн Yoti

  • Пользователь
  • Сообщений: 4477
  • Пол: Мужской
  • Не тро-гай ме-ня
    • Steam
    • Просмотр профиля
Ага... в процессе запиливания стало понятно что зеленый цвет на голубом фоне флага виден не очень. Значит надо сделать переключатель какого цвета будет
Почему просто не инвертировать цвет?

Оффлайн SeregaZ

  • Пользователь
  • Сообщений: 2538
  • Пол: Мужской
  • ливнул с форума
    • Youtube
    • Просмотр профиля
ну... проще все-таки обычная линия, нежели еще один цикл, где бы проверялась каждая точка на пути этой линии, инвертировался цвет этой точки и перерисовывался. нужен как можно простой код, чтобы новичку было легче вкатывать в тему. а там уже когда наш подозреваемый втянется в вопрос и откроет тайну вселенной, что программировать на самом деле не сильно сложно - то сам уже сможет и инвертирование и блек джек и легкодоступных женщин там напрограммировать.