Введение
Всем привет.
В данной теме мы обсудим как делать перевод на GameCube, разберём одну игру, на отличный результат не претендуем, хотя бы попробуем научиться работать с файлами GC.
Игра для опытов - Spawn Armageddon.
P.S. Данная тема будет пополняться по мере взлома игры.
Так с, для начала нам нужен софт:
- Программа для работы с графикой GameCube
- Программа для работы с образами GameCube
- Photoshop или любой другой редактор изображений, поддерживающий TGA
- HEX редактор
- Тайловый редактор
- *Знания программирования, компилятор Delphi XE7(Да, да, я использую Delphi)
- Эмулятор GameCube
На счёт первого, нам понадобится TPL Convert, лучше с генератором конфигов от DND1, чтобы самим не писать.
Так же для удобности можно установить CTools. Не уверен, что нам может понадобиться программа для работы с TPL, т.к. ещё не знаю есть ли TPL в той игре, которую будем разбирать.
На счёт второго, есть 2 программы для работы с образами GC(GameCube), мы будем пользоваться GameCube Rebuilder, т.к. он умеет пересобирать образы.
На счёт четвёртого, я использую HxD, но подойдёт любой.
На счёт пятого, я использую CrystalTile2, если у вас нет привязанности к другому тайловому редактору, то пользуйтесь им.
И на счёт шестого, данный пункт не обязателен, но в процессе разбора игры мы будем писать программы для распаковки и т.п.
P.S. На код не ругайтесь, учусь, как ни как.
TPL Convert, CTools, GameCube Rebuilder и CrystalTile2 можно загрузить на сайте замечательного человека Anton : Zelda64Rus
Ну что ж, приступим.
ЧАСТЬ 1 - Распаковка и осмотр файлов
Для начала открываем наш GCM(GameCube Rebuilder) и видим данное окно:
Немного расскажу о функциях:
Root->Open - Открывает Root дирректорию распакованого образа(нужно для пересборки)
Root->Save - Выбор дирректории и имя будущего образа
Root->Close - Закрывает открытую Root дирректорию
Root->Rebuild - Собирает новый образ
Root->Exit - Выход из программы
Image->Open - Открывает образ
Image->Close - Закрывает открытый образ
Image->Wipe garbage - Удаляет мусор в образе
Image->Exit - Выход из программы
Options->Modify System Files - Наверняка активация модификации системный файлов(?)
Options->Do not use 'game.toc' - Походу, при активации отключает использование таблицы TOC(?)
Help->About - Об авторе
Узнав, для чего какая функция, откроем образ:
Клацаем Image->Open
Если у вас расширение у образа ISO, то просто ищем ваш образ и открываем его в программе, но если у вас расширение GCM, то меняем аттрибут GameCube ISO(*.iso) на GameCube Image File(*.gcm)
После открытия образа видим данное окно(Не думаю, что файлы сильно будут отличаться от PAL версии):
Жмём правой кнопкой на "root" в таблице файлов и жмём Export, выбираем куда сохранить папку "root" и ждём конца распаковки, по окончанию распаковки появится сообщение "Done". После этого можем приступать к осмотру файлов.
Так, файлы распакованы, HEX редактор и Тайловый редактор готов, можем приступать.
Открываем папку "root"(предварительно перейдя в ту папку, куда вы извлекли файлы образа).
Видим такие файлы:
Немного о расширениях:
H4M - Видео формат Gamecube
THP - Видео формат GameCube и Wii
DOL - то же самое, что и EXE в Windows, то бишь исполняемый файл.
DSP - чаще, звук GameCube
TPL - Графика GameCube
ARC - архив GameCube, может быть RARC, может YAZ, может U8, а может вообще свой формат.
Открываем папку "DATA" и смотрим файлики, видим несколько THP файлов, то бишь видео файлов, всё остальное WAD и CSF, следовательно нам они и нужны, но какие именно? Их же куча.
Об этом поговорим в следующей части, скажу лишь вот что, в папке DATA есть файлы с префиксом en_*.wad(где * - любые другие символы). Так что они первые будут подвергнуты "взлому".
Ну а сейчас поставим цели:
- Найти шрифт
- Разобрать и написать софт для работы с WAD и CSF
- Найти текст и разобрать его
- Определить формат текстур
На счёт шрифта, файла с именем FONT или подобного нету, но у меня есть предположения на некоторые файлы
- Start.DOL - Данный файл ВСЕГДА надо проверять на наличие текста и шрифта(если он не найден до этого) !ЗАПОМНИТЕ!
- System.WAD
- Global.WAD
- Arial3d.WAD
Данные файлы мы тоже проверим, но в следующей части.
Если вам понравилася данная тема и вы хотите видеть продолжение, то отписывайтесь, продолжим
ЧАСТЬ 2 - Разбираем WAD
P.S. Прошу Админа данный текст скопировать в шапку и удалить это сообщение.
Я решил для начала разобрать WAD, чтобы уже рассмотреть по отдельности каждый файл в архиве.
Начнём с открытия файла в HEX редакторе, давайте ка откроем файл System.WAD и посмотрим содержимое:
А теперь расскажу о том, что я выделил:
Чёрный - TOC, можно использовать как проверку, тот ли архив открываем. Никогда не меняется. Скорей всего указывает на блок TOC.
Тёмно-серый - Размер блока TOC, блок TOC начинается c $1C.(Значение, перед которым стоит "$", означает HEX значение)
Серый - Кол-во файлов.
А дальше идёт TOC, на каждый файл выделяется $20, то есть будет цикл считывания от 1 до Кол-во файлов.
Красный - Имя файла, максимальный размер $10.
Оранжевый - Расширение файла, максимальный размер $4.
Жёлтый - Поинтер(указатель) на начала блока с файлом.
Синий - Размер блока с файлом.
А вот теперь последнее:
Коричневый - Перед каждым блоком с файлом есть информация в размере $1C, посмотрев на эти $1C, можно понять, что это информация о блоке, который идёт после неё. Она нам не нужна, т.к. мы считали эту информацию из TOC, но работать с ней придётся, т.к. это понадобится для запаковки
Разобрав этот архив, приступим к написанию программы для распаковки.
Я составил структуру WAD, для удобности:
Type TTOCBlock = record NameFile : array[1..199] of AnsiString;//Имя файла ExtFile : array[1..199] of AnsiString;//Расширение файла PointerFile : array[1..199] of integer;//Позиция файла SizeFile : array[1..199] of integer;//размер файла end; Type TInfoBlock = record NameFile : array[1..199] of AnsiString;//Имя файла ExtFile : array[1..199] of AnsiString;//Расширение файла SizeFile : array[1..199] of integer;//размер файла end; Type TWADFile = record//Big-Endian CheckTOC : AnsiString;//Переходим на $10 и читаем 3 байта в String, если String = 'TOC', то позиция стримки + 1 SizeTOCBlock : Integer;//Размер TOC CountFiles : Integer;//Кол-во файлов TOCBlock : TTOCBlock; InfoBlock : TInfoBlock; end;
Ну что ж, начнём писать программу. Весь код я постараюсь комментировать, для удобности.
Открываем Delphi XE7(можно и постарее, просто у меня стоит именно эта версия), создаём VCL Forms Application(Да, я люблю именно программы на формах) и копируем мной составленную структуру выше "var":
(старый скрин, вместо string - AnsiString)
После "var" напишем
WADFile : TWADFile;
F:TFileStream;
Так же, после {$R *.dfm} добавим такой код:
function SwapEndian(Value: integer): integer; register; overload;
asm
bswap eax
end;
Он понадобится, чтобы конвертировать Little-Endian значение в Big-Endian и наоборот.
Теперь добавим на форму OpenDialog, SaveDialog, MainMenu, StringGrid, StatusBar и ProgressBar.
Стандартное имя компонентов я менять не буду, чтобы вы не запутались.
Так, начнём с оформления:
Меню я сделал такое, пока сойдёт
Теперь настроим StringGrid:
Align = alClient
ColCount = 3
FixedCols = 0
Options -> goRowSelect = Включить
RowCount = 2
Так же добавим 3 панели у StatusBar
У 1 панели ставим Width = 100, у 2 Width = 300 и Text="File:", у 3 Width Можно любой, но не меньше 100, а Text="Mode:".
Так, вроде всё, перейдём к написанию кода, создадим у Form1 процедуру OnCreate(Надеюсь, вы умеете самое простое).
Пишем туда такой код:
begin with ProgressBar1 do//Добавляем progressbar на 1 панель statusbar begin Parent := StatusBar1; Position := 0; Top := 2; Left := 1; Height := StatusBar1.Height - Top; Width := StatusBar1.Panels[0].Width - Left; end; StringGrid1.Cells[0,0]:='Name File';//Пишем текст в первую строку StringGrid1.Cells[1,0]:='Pointer'; StringGrid1.Cells[2,0]:='Size'; StringGrid1.ColWidths[0]:=450;//Меняем размер у колонок StringGrid1.ColWidths[1]:=80; StringGrid1.ColWidths[2]:=80; end;
Так же у формы создадим процедуру OnCanResize и напишем туда этот код:
StringGrid1.ColWidths[0]:=StringGrid1.Width-(StringGrid1.ColWidths[1]+StringGrid1.ColWidths[2]+10);//При изменении размера формы, расширяем колонку с названием файла
Ещё добавим фильтр у OpenDialog
WAD File (*.wad)|*.wad
Вот и всё, с оформлением пока всё.
Ну что ж, начнём писать процедуру распаковки:
Давайте ка я распишу как будет работать распаковка:
- Переходим на $10 и читаем 3 байта, если это TOC, то продолжаем работу и , если нет, то выводим сообщение об ошибке.
- К позиции стримки(Stream) прибавляем 1 и считываем 4 байта, конвертируем из Big-Endian в Little-Endian с помощью функции, которую мы написали с самого начала (SwapEndian) и сохраняем в WADFile.SizeTOCBlock.
- Считываем 4 байта, конвертируем из Big-Endian в Little-Endian и сохраняем в WADFile.CountFiles.
- Начинаем цикл с 1 до WADFile.CountFiles и в цикле считываем по одному байту, пока не встретится $00, сохраняем размер от прошлой позиции стримки до $00, читаем в WADFile.TOCBlock.NameFile столько, сколько получили при считывании до позиции $00. Дальше переходим к позиции расширения и считываем его в WADFile.TOCBlock.ExtFile. Потом читаем 4 байта, конвертируем, записываем в WADFile.TOCBlock.PointerFile, читаем ещё 4 байта, конвертируем, сохраняем в WADFile.TOCBlock.SizeFile, добавляем к позиции стримки 4, добавляем всё данные в StringGrid и на этом цикл кончается.
А теперь пишем процедуру для распаковки:
Добавим
procedure ExtractWad; procedure ExtractBlock(NumFile:Integer);
в Type формы(в самом верху)
А теперь добавим саму процедуру распаковки:
Procedure TForm1.ExtractWad;//Узнаём всё о файлах var I,TempPos,Size:Integer; B:Byte; S:String; begin F:=TFileStream.Create(OpenDialog1.FileName,fmOpenReadWrite); F.Position:=$10; SetLength(WADFile.CheckTOC,3); F.Read(WADFile.CheckTOC[1],3); F.Position:=F.Position+1; if WADFile.CheckTOC='TOC' then begin F.Read(WADFile.SizeTOCBlock,4); WADFile.SizeTOCBlock:=SwapEndian(WADFile.SizeTOCBlock); F.Read(WADFile.CountFiles,4); WADFile.CountFiles:=SwapEndian(WADFile.CountFiles); StringGrid1.RowCount:=WADFile.CountFiles+1; StatusBar1.Panels[2].Text:='Mode:WAD File'; StatusBar1.Panels[1].Text:='File:'+OpenDialog1.FileName; for I := 1 to WADFile.CountFiles do begin TempPos:=F.Position; Size:=1; F.Read(B,1); while B<>$00 do begin F.Read(B,1); Size:=Size+1; end; Size:=Size-1; F.Position:=TempPos; SetLength(WADFile.TOCBlock.NameFile[i],Size); F.Read(WADFile.TOCBlock.NameFile[i][1],Size); while b=$00 do f.Read(b,1); f.Position:=f.Position-1; SetLength(WADFile.TOCBlock.ExtFile[i],3);//временно F.Read(WADFile.TOCBlock.ExtFile[i][1],3); f.Position:=f.position + 1; F.Read(WADFile.TOCBlock.PointerFile[i],4); WADFile.TOCBlock.PointerFile[i]:=SwapEndian(WADFile.TOCBlock.PointerFile[i]); F.Read(WADFile.TOCBlock.SizeFile[i],4); WADFile.TOCBlock.SizeFile[i]:=SwapEndian(WADFile.TOCBlock.SizeFile[i]); F.Position:=F.Position+4; StringGrid1.Cells[0,i]:=WADFile.TOCBlock.NameFile[i]+'.'+WADFile.TOCBlock.ExtFile[i]; StringGrid1.Cells[1,i]:='0x'+IntToHex(WADFile.TOCBlock.PointerFile[i],WADFile.TOCBlock.PointerFile[i].Size); StringGrid1.Cells[2,i]:='0x'+IntToHex(WADFile.TOCBlock.SizeFile[i],WADFile.TOCBlock.SizeFile[i].Size); end; end else ShowMessage('Это не WAD:('); end; procedure TForm1.ExtractBlock(NumFile: Integer);//Извлечение определённого блока var Temp:TFileStream; I:Integer; begin SaveDialog1.FileName:=WADFile.TOCBlock.NameFile[NumFile]; SaveDialog1.DefaultExt:=WADFile.TOCBlock.ExtFile[NumFile]; if not SaveDialog1.Execute then exit else begin f.Position:=WADFile.TOCBlock.PointerFile[NumFile]; ShowMessage(IntToHex(WADFile.TOCBlock.SizeFile[NumFile],4)); Temp:=TFileStream.Create(SaveDialog1.FileName,fmCreate); Temp.CopyFrom(F,WADFile.TOCBlock.SizeFile[NumFile]); Temp.Free; end; end;
Теперь на кнопку Файл->Открыть повесим код
if not OpenDialog1.Execute then exit else begin ExtractWad; end;
А на кнопку Архив->Извлечь файл повесим код
ExtractBlock(StringGrid1.Row);
Я ещё этот код повесил на двойной клик по StringGrid, мне так удобней
Ну, вроде, всё работает
Давайте напишем ещё функцию извлечения всех файлов и пойдём пить чай с тортиком.
Так, сначала в Uses добавим ShlObj, ActiveX, потом в код добавим функцию(лучше в начало):
function AdvSelectDirectory(const Caption: string; const Root: WideString; var Directory: string; EditBox: Boolean = False; ShowFiles: Boolean = False; AllowCreateDirs: Boolean = True): Boolean; function SelectDirCB(Wnd: HWND; uMsg: UINT; lParam, lpData: lParam): Integer; stdcall; var PathName: array[0..MAX_PATH] of Char; begin case uMsg of BFFM_INITIALIZED: SendMessage(Wnd, BFFM_SETSELECTION, Ord(True), Integer(lpData)); end; Result := 0; end; var WindowList: Pointer; BrowseInfo: TBrowseInfo; Buffer: PChar; RootItemIDList, ItemIDList: PItemIDList; ShellMalloc: IMalloc; IDesktopFolder: IShellFolder; Eaten, Flags: LongWord; const BIF_USENEWUI = $0040; BIF_NOCREATEDIRS = $0200; begin Result := False; if not DirectoryExists(Directory) then Directory := ''; FillChar(BrowseInfo, SizeOf(BrowseInfo), 0); if (ShGetMalloc(ShellMalloc) = S_OK) and (ShellMalloc <> nil) then begin Buffer := ShellMalloc.Alloc(MAX_PATH); try RootItemIDList := nil; if Root <> '' then begin SHGetDesktopFolder(IDesktopFolder); IDesktopFolder.ParseDisplayName(Application.Handle, nil, POleStr(Root), Eaten, RootItemIDList, Flags); end; OleInitialize(nil); with BrowseInfo do begin hwndOwner := Application.Handle; pidlRoot := RootItemIDList; pszDisplayName := Buffer; lpszTitle := PChar(Caption); ulFlags := BIF_RETURNONLYFSDIRS or BIF_USENEWUI or BIF_EDITBOX * Ord(EditBox) or BIF_BROWSEINCLUDEFILES * Ord(ShowFiles) or BIF_NOCREATEDIRS * Ord(not AllowCreateDirs); lpfn := @SelectDirCB; if Directory <> '' then lParam := Integer(PChar(Directory)); end; WindowList := DisableTaskWindows(0); try ItemIDList := ShBrowseForFolder(BrowseInfo); finally EnableTaskWindows(WindowList); end; Result := ItemIDList <> nil; if Result then begin ShGetPathFromIDList(ItemIDList, Buffer); ShellMalloc.Free(ItemIDList); Directory := Buffer; end; finally ShellMalloc.Free(Buffer); end; end; end; Так, теперь в Type формы добавим: procedure ExtractWadAll(Path:String);
И начнём писать функцию для распаковки всех файлов:
Procedure TForm1.ExtractWadAll(Path:String); var I:Integer; Temp:TFileStream; begin ProgressBar1.Max:=WADFile.CountFiles; if path<>'' then for I := 1 to WADFile.CountFiles do begin F.Position:=WADFile.TOCBlock.PointerFile[i]; Temp:=TFileStream.Create(Path+'\'+WADFile.TOCBlock.NameFile[i]+'.'+WADFile.TOCBlock.ExtFile[i],fmcreate); Temp.CopyFrom(F,WADFile.TOCBlock.SizeFile[i]); Temp.Free; ProgressBar1.Position:=i; end; ProgressBar1.Position:=0; ShowMessage('Готово'); end; Осталось только добавить на кнопку извлечения всех файлов вот этот код: var s : string; begin AdvSelectDirectory('Выберите папку для распаковки', '', s, False, False, True); ExtractWadAll(s); end;
Всё, готово, радуемся от того, какие мы молодцы.
На этом урок заканчивается, в следующем уроке мы продолжим разработку программы и может быть начнём искать шрифт или текст, спасибо за чтение
Если будут вопросы - спрашивайте.
Исходники данного проекта в прикреплённых файлах, под именем SAEditorSourceLesson2.rar
Ваш, TTEMMA
Прикрепленные файлы
- SAEditorSourceLesson2.rar 763,72К 402 Количество загрузок: