Список форумов shedevr.org.ru shedevr.org.ru
Группа перевода приставочных игр "ШЕДЕВР"
 
 FAQFAQ   ПоискПоиск   ПользователиПользователи   ГруппыГруппы   РегистрацияРегистрация 
 ПрофильПрофиль   Войти и проверить личные сообщенияВойти и проверить личные сообщения   ВходВход 

Дополнительные возможности. Написание плагинов.

 
Начать новую тему   Ответить на тему    Список форумов shedevr.org.ru -> Утилита Kruptar
Предыдущая тема :: Следующая тема  
Автор Сообщение
Chime[RUS]



Зарегистрирован: 31.08.2014
Сообщения: 11

СообщениеДобавлено: Пн Фев 15, 2016 11:15 pm    Заголовок сообщения: Дополнительные возможности. Написание плагинов. Ответить с цитатой

Документация которую написал Griever была для конкретного случая запаковки. Но прочитать его всё равно полезно, т.к. там подробно описано назначение функций. )

Плагины же бывают разными.
Я попытаюсь систематизировать разновидность одного типа. Он не сложный, но очень неприятно когда проект не работает из-за такой мелочи, как неверно определённый символ разрыва строки. Такое случается довольно часто. Специальные коды в играх иногда содержат довольно много символов и если среди них есть тот, что в таблице обозначен как конец строки, Kruptar режет извлекаемую строку на этом коде и часть данных теряется. Чтобы этого избежать люди придумали вносить подобные коды в исключения при чтении скрипта (см. плагин Griever'а для TP).

На самом деле гораздо проще и правильнее не каждый такой код вносить в исключения, а вычитать из обработки сразу целые семейства кодов. Этому посвящены две темы на форуме MagicTeam: 1 и 2.

Необходимый навык - правильное распознавание контрольных кодов (приходит самостоятельно вместе с опытом: работа с WildCard в Hex-редакторе например или более продвинутые методы).

При составлении таблицы, способ описан в предыдущем руководстве, выясняется нужен ли Kruptar'у плагин или нет.
Если он таки нужен, то понадобится Delphi (Lazarus не пожелал обрабатывать ассемблерные вставки из Needs.pas). Но если очень хочется можно и на Си писать.

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

Рассматривать будем только функции GetData и GetStrings, т.к. меняются только они и название плагина.
Вот пример плагина для однобайтовой кодировки (здесь Zelda OoT N64) и фрагмент таблицы:

Код:
function GetData(TextStrings: PTextStrings): AnsiString; stdcall;
var
 R: PTextString;
begin
 Result := '';
 If TextStrings = NIL then Exit;
 With TextStrings^ do
 begin
  R := Root;
  While R <> NIL do
  begin
   Result := Result + R^.Str + #2; // код конца строки: 02, можно его и не отображать в проекте.
   R := R^.Next
  end
 end
end;

Function GetStrings(X, Sz: Integer): PTextStrings; stdcall;
var
 P: PByte; PC: PChar absolute P;
 Len: Byte; Code: Byte;
begin
 New(Result, Init);
 with Result.Add^ do
 begin
  P := Addr(ROM[X]);
  while P^ <> 0 do
  begin
  Len := 0;      //Обозначаем переменную длины кода, а то вдруг их там нет.
   if (P^ = $05) or (P^ = $06) or (P^ = $07) //Здесь идёт перечисление всех первых байтов контрольных кодов (берутся из таблицы)
   or (P^ = $0C) or (P^ = $0E) or (P^ = $11) //Если встречается кто-то из них, то нужно прочитать контольный код
   or (P^ = $12) or (P^ = $13) or (P^ = $14)
   or (P^ = $15) or (P^ = $1E)
   then begin
    Code := P^; // Запоминаем этот первый байт
    Str := Str + PC^; //Записываем его в извлечённый текст (Вот он 1 байт, который не будем учитываеть в Len)
    Inc(P); //Переходим к следующему
    case Code of   //Т.к. зависимость длины кода от каких-то байт в нём обнаружена не была, просто выбираем.
     $05,
     $06,
     $0C,
     $0E,
     $13,
     $14,
     $1E: Len := 1; //Если код начинается с $05, $06, $0C, $0E, $13, $14 или $1E то его длина 2 байта, осталось 1 байт считать.
     $07,
     $11,
     $12: Len := 2; //Если код начинается с $07, $11, $12 то длина кода 3, следовательно нужно прочитать ещё 2 байта.
     $15: Len := 3 //Если код начинается с $15 то длина его 4, т.е. осталось считать 3 байта.
    end;
    X := Length(Str); //
    SetLength(Str, X + Len);    //
    Move(P^, Str[X + 1], Len); //
    Inc(P, Len); //Здесь считываем нужное количество байт.
   end else
    if P^ = $02 then Break // это можно написать и здесь: while P^ <> $02 do, но мне обычно лень, т.к. ноль не мешает.
   else
   begin
    Str := Str + PC^; // Обычное чтение, когда никакие контрольные коды не встретились.
    Inc(P)
   end
  end
 end
end;


Фрагмент таблицы
Код:
0540=[/C]
0541=[Red]
0542=[Green]
0543=[Blue]
0544=[l_Blue]
0545=[Pink]
0546=[Yellow]
0547=[Black]
0602=[sp_01]
0603=[sp_02]
077072=[07_200]
0C0A=[delay_1]
0C14=[delay_2]
1100A0=[11_223]
123880=[snd_04]
1300=[icon_01]
1301=[icon_02]
1302=[icon_03]
1303=[icon_04]
1400=[/slow]
1401=[slow_01]
1402=[slow_02]
1403=[slow_03]
15000110=[BG1]
15002000=[BG2]
1E00=[score_HAR]
1E01=[score_Poe]
1E02=[score_fish]
1E03=[score_Epona]

01
04
ends
02


Вот пример чуть более хитрого плагина для однобайтовой кодировки (здесь Zelda MC GBA):

Код:
Function GetData(TextStrings: PTextStrings): AnsiString; stdcall;
var
 R: PTextString;
begin
 Result := '';
 If TextStrings = NIL then Exit;
 With TextStrings^ do
 begin
  R := Root;
  While R <> NIL do
  begin
   Result := Result + R^.Str + #0; // код конца строки: 00, можно его и не отображать в проекте.
   R := R^.Next
  end
 end
end;

Function GetStrings(X, Sz: Integer): PTextStrings; stdcall;
var
 P: PByte; PC: PChar absolute P;
 Len: Byte; Code: Byte;
begin
 New(Result, Init);
 with Result.Add^ do
 begin
  P := Addr(ROM[X]);
  while P^ <> 0 do
  begin
  Len := 0; //Обозначаем переменную длины кода, а то вдруг их там нет.
   if (P^ = $01) or (P^ = $02) or (P^ = $03)    //Опять идёт перечисление всех первых байтов контрольных кодов
   or (P^ = $04) or (P^ = $05) or (P^ = $06) //Если встречается кто-то из них, то нужно прочитать контольный код
   or (P^ = $07) or (P^ = $08) or (P^ = $09)
   or (P^ = $0C) or (P^ = $0F)
   then begin
    Code := P^; // Запоминаем первый байт
    Str := Str + PC^; //Записываем его в извлечённый текст
    Inc(P); //Переходим к следующему
    case Code of //Т.к. зависимость длины кода от каких-то байт в нём обнаружена не была, просто выбираем.
     $01,
     $02,
     $06,
     $08,
     $09,
     $0C,
     $0F: Len := 1; //Длина этих кодов 2 байта, 1 уже был считан.
     $03,
     $07: Len := 2; //Длина этих кодов 3 байта, 1 уже был считан.
     $04: case P^ of //А вот если код начинается с $04 то тут не всё так однозначно.
             $10: Len := 2; //Если следом за $04 идёт $10 длина такого кода будет 3.
             else Len := 1 //В любом другом случае 2.
          end;
     $05: case P^ of   //Аналогично поступаем и в этом случае
             $FF: Len := 1; //Если следом за $05 идёт $FF длина такого кода будет 2.
             else Len := 2 //В любом другом случае 3.
          end
    end;
    X := Length(Str); //
    SetLength(Str, X + Len);    //
    Move(P^, Str[X + 1], Len); //
    Inc(P, Len); //Здесь считываем нужное количество байт.
   end else
   begin
    Str := Str + PC^; //Обычное чтение, когда никакие контрольные коды не встретились.
    Inc(P)
   end
  end
 end
end;


Фрагмент таблицы
Код:
0101=[01_001]
0200=[/C]
0201=[Red]
0202=[Green]
0203=[Blue]
0300DB=[03_045]
0301FE=[03_077]
0301FF=[03_078]
030200=[03_079]
030201=[03_080]

041000=[04_094]
041006=[04_095]
041007=[04_096]
04100C=[04_097]
04100E=[04_098]
0412=[04_099]
0413=[04_100]
0414=[04_101]
0415=[04_102]

050309=[Sw_News_1p1]
05465F=[05_189]
05FF=[05_190]

0600=[Name]
0601=[Choice_1]
0602=[Choice_2]
0603=[Choice_3]
070800=[07_201]
071005=[07_202]
08FF=[08_235]
0900=[09_236]
0978=[09_237]
0C00=[A]
0C01=[B]
0C02=[L]
0C03=[R]

0A
ends
00


Совсем недавно увидел ещё более хитрый подход: байт окончания строки 00, системные коды могут не просто его содержать, а с него начинаться(!), но там можно зацепиться за 3-ий байт (вроде всегда одинаковый). Плагин осилил. + Добавился ещё один его собрат.

Код:
Function GetData(TextStrings: PTextStrings): AnsiString; stdcall;
var
 R: PTextString;
begin
 Result := '';
 If TextStrings = NIL then Exit;
 With TextStrings^ do
 begin
  R := Root;
  While R <> NIL do
  begin
   Result := Result + R^.Str + #0; // код конца строки: 00 не отображается в проекте.
   R := R^.Next
  end
 end
end;

Function GetStrings(X, Sz: Integer): PTextStrings; stdcall;
var
 P: PByte; PC: PChar absolute P;
 Len: Integer; Code: Byte;
begin
 New(Result, Init);
 with Result.Add^ do
 begin
  P := Addr(ROM[X]);
  while 2+2=4 do   //Рыбу оставляем, но шунтируем
  begin
  Len := 0;
   if (P^ = $01) or (P^ = $02) or (P^ = $03)
   then begin  // Считать 1 байт в строку
    Code := P^;
    Str := Str + PC^;
    Inc(P);
    case Code of
     $01,
     $02,
     $03: Len := 1;
    end;
    X := Length(Str);
    SetLength(Str, X + Len);
    Move(P^, Str[X + 1], Len);
    Inc(P, Len);
   end else
   if P^ = $00  //Проверяем этот код отдельно
   then begin
        Inc(P, 2);
        Code := P^;
        dec(P, 2);
        if Code = $0E //Если второй символ после него 0E, то это хитрый код и его нужно записать.
        then begin
        Str := Str + PC^; Inc(P);
        Str := Str + PC^; Inc(P)
             end else break //Если нет - конец строки без записи и выход.
        end else
   begin
    Str := Str + PC^;
    Inc(P)
   end
  end
 end
end;


Фрагмент таблицы
Код:
0E=[0E]
0100=[0_1]
0200=[0_2]
0300=[0_3]
0101=[1_1]
0201=[1_2]
0301=[1_3]
0002=[2_0]
0102=[2_1]
0202=[2_2]
0302=[2_3]
8102=[2_81]
8202=[2_82]
8302=[2_83]
8402=[2_84]
8502=[2_85]
ends
00


Тот же тип с той лишь разницей, что здесь всегда будем писать 00, т.к. он является кодом пробела, а раз так, то и код окончания строки - 0000 будет в проекте отображаться.

Код:
Function GetStrings(X, Sz: Integer): PTextStrings; stdcall;
var
 P: PByte; PC: PChar absolute P;
 Len: Integer; Code: Byte;
begin
 New(Result, Init);
 with Result.Add^ do
 begin
  P := Addr(ROM[X]);
  while  2+2=4 do   //Рыбу оставляем, но шунтируем
  begin
  Len := 0;
   if  (P^ = $00) or (P^ = $E5) or (P^ = $E7) or (P^ = $E9)
   or (P^ = $EC) or (P^ = $EE) or (P^ = $ED) or (P^ = $F2)
   or (P^ = $F4) or (P^ = $F5) or (P^ = $F8) or (P^ = $FB)
   or (P^ = $FC) or (P^ = $FF)
   then begin  // Считать 1 байт в строку
    Code := P^;
    Str := Str + PC^;
    Inc(P);
    case Code of
     $E5,
     $EE,
     $F2,
     $F5,
     $F8,
     $FC: Len := 1;
     $E7,
     $E9,
     $EC,
     $FF: Len := 2;
     $ED,
     $FB: Len := 3;
     $F4: Len := 5;
     $00: case P^ of
             $00: begin Str := Str + PC^; break //Конец строки пишем и выходим
                  end;
             else Len := 0 //Т.к. 00, т.е. пробел уже записан.
          end;
    end;
    X := Length(Str);
    SetLength(Str, X + Len);
    Move(P^, Str[X + 1], Len);
    Inc(P, Len);
   end else
   begin
    Str := Str + PC^;
    Inc(P)
   end
  end
 end
end;


Фрагмент таблицы
Код:
00=
01=0
02=1
...
E500=!
E501=‼
E502=?
...
E70000=[E700]
E70100=[E701]
E70500=[E705]
E90000=[E900]
F200=[F200]
F404121219FF=[F401]
F500=[F500]
EC0000=[EC00]
ED002200=[ED00]
FB040050=[FB04]
FF0000=[FF00]

E8
EA
EB
ends
0000


Вот пример плагина для однобайтовой кодировки c кодами содержащими в себе поле размера (здесь Зельды с GC, Wii и WiiU в которых используются bmg контейнеры для хранения текста: WW, FSA, TP, TP_HD. ):

Код:
Function GetData(TextStrings: PTextStrings): String; stdcall;
Var
 R: PTextString;
begin
 Result := '';
 If TextStrings = NIL then Exit;
 With TextStrings^ do
 begin
  R := Root;
  While R <> NIL do
  begin
   Result := Result + R^.Str + #0; // т.к. код конца строки всего один, можно его и не отображать в проекте.
   R := R^.Next
  end
 end
end;

Function GetStrings(X, Sz: Integer): PTextStrings; stdcall;
var
P: PByte; PC: PChar absolute P;
Len: Byte; Code: Byte;
begin
 New(Result, Init);
 with Result.Add^ do
 begin
  P := Addr(ROM[X]);
  while P^ <> 0 do
  begin
   if P^ = $1A then //Более продвинутая система кодов: все начинаются с $1A и содержат в себе размер.
   begin
    Str := Str + PC^; //Читаем $1A
    Inc(P); //Переходим к следующему байту
    Code := P^; //Запоминаем байт идущий после $1A, т.к. он и является размером кода.
    Str := Str + PC^; //Читаем байт размера в проект
    Inc(P); //Переходим к следующему
    Len := Code - 2; //Можно прочитать оставшиеся байты в строку (т.к. два байта уже прочитаны от размера отнимаем 2)
    X := Length(Str); //
    SetLength(Str, X + Len);    //
    Move(P^, Str[X + 1], Len); //
    Inc(P, Len) //Здесь считываем нужное количество байт.
   end else
    begin
      Str := Str + PC^; //Обычное чтение, когда никакие контрольные коды не встретились.
      Inc(P)
    end
  end
 end
end;


Фрагмент таблицы
Код:
1A05000000=[Name]
1A0500000D=[L]
1A0500000E=[R]
1A0500000F=[X]
1A05000010=[Y]
1A05000011=[Z]
1A06FF000000=[/c]
1A06FF000001=[Red]
1A06FF000002=[Green]
1A06FF000003=[Blue]
1A06FF000004=[Yellow]
1A06FF000005=[l_Blue]
1A06FF000006=[Purple]
1A06FF000007=[Silver]
1A06FF000008=[Orange]
1A070200060100=[07_101]
1A08020024000101=[08_120]
1A0902000500000166=[09_135]
1A0A0200090000037401=[0A_232]
1A0B020026000000140A01=[0B_312]
1A0C02000D00000032010000=[0C_313]
1A0D02002C000001400000013C=[0D_319]
1A0E020007000000015C0000015C=[0E_327]
1A0F02001002000001C3000001C400=[0F_379]
1A10020023000000000002EF000002EE=[10_423]
1A1102001D000000320000039C0000039B=[11_532]
1A150200180000013A0000013C0000013C0000013D=[15_535]

0A
ends
00


Вот пример плагина для двубайтовой кодировки c кодами содержащими в себе поле размера (здесь Зельды NDS: PH и ST в которых используются bmg контейнеры для хранения текста, но текст в Unicode. ):

Код:
function GetData(TextStrings: PTextStrings): AnsiString; stdcall;
var
 R: PTextString;
begin
 Result := '';
 If TextStrings = NIL then Exit;
 With TextStrings^ do
 begin
  R := Root;
  While R <> NIL do
  begin
   Result := Result + R^.Str + #0 + #0; // код конца строки: 0000, можно его и не отображать в проекте.
   R := R^.Next
  end
 end
end;

function GetStrings(X, Sz: Integer): PTextStrings; stdcall;
type
 TWordRec = packed record
  A, B: Char
 end;
var
 PW: PWord; PB: PByte absolute PW; PC: ^TWordRec absolute PW;
 Len: Word; Code: Byte;
begin
 New(Result, Init);
 with Result.Add^ do
 begin
  PW := Addr(ROM[X]);
  while PW^ <> 0 do
  begin
   if PW^ = $001A then //Система кодов с $1A в двубайтовом варианте (Здесь LE).
   begin
    Str := Str + PC.A + PC.B; // Читаем 2 байта: $001A.
    Inc(PW); //Переходим к следующему слову.
    Code := PB^; //Запоминаем размер, кодов больше 255 не встречал, поэтому размер здесь определён только 1 байтом.
    Str := Str + PC.A + PC.B; //Читаем 2 байта размера в проект
    Inc(PW); //Переходим к следующему слову.
    Len :=  Code - 4; //Можно прочитать оставшиеся байты в строку
    X := Length(Str); //
    SetLength(Str, X + Len);    //
    Move(PB^, Str[X + 1], Len); //
    Inc(PB, Len) //Здесь считываем нужное количество байт.
   end else
   begin
    Str := Str + PC.A + PC.B; //Обычное чтение, когда никакие контрольные коды не встретились.
    Inc(PW)
   end
  end
 end
end;


Фрагмент таблицы
Код:
1A0006000000=[Ch1]
1A0006FE0000=[Name]
1A0008000A000000=[08_012]
1A0008FF00000000=[/c]
1A0008FF00000100=[Red]
1A0008FF00000200=[Green]
1A0008FF00000300=[Blue]
1A000A000E0014000000=[0A_128]
1A000CFF02000159306A3000=[0C_191]
1A000EFF02000044306130693000=[0E_192]
1A0010FF020001553093304B304F3000=[10_193]
1A00220401000900770069006E0073000000770069006E000000770069006E007300=[Wins]
1A00280401000000740069006D00650073000000740069006D0065000000740069006D0065007300=[Times]
1A002E04010000005200750070006500650073000000520075007000650065000000520075007000650065007300=[Rupees]
1A003404010001007300650063006F006E006400730000007300650063006F006E00640000007300650063006F006E0064007300=[Seconds]
1A0040040100000070006F007300740063006100720064007300000070006F00730074006300610072006400000070006F007300740063006100720064007300=[Postcards]
1A004C04010000007300750062006D0069007300730069006F006E00730000007300750062006D0069007300730069006F006E0000007300750062006D0069007300730069006F006E007300=[Submissions]

0A00
ends
0000


Вот пример плагина для двубайтовой кодировки c кодами содержащими в себе поле размера (здесь Зельды SS и WW_HD (UTF-16 BE), ALBW и TFH (UTF-16 LE) в которых используются msbt контейнеры для хранения текста):
Код:
function GetData(TextStrings: PTextStrings): AnsiString; stdcall;
var
 R: PTextString;
begin
 Result := '';
 If TextStrings = NIL then Exit;
 With TextStrings^ do
 begin
  R := Root;
  While R <> NIL do
  begin
   Result := Result + R^.Str + #0 + #0;
   R := R^.Next
  end
 end
end;

function SwapMe2(V: Word): Word;
  asm xchg al,ah end;

function GetStrings(X, Sz: Integer): PTextStrings; stdcall;
type
 TWordRec = packed record
  A, B: Char
 end;
var
 PW: PWord; PB: PByte absolute PW; PC: ^TWordRec absolute PW;
 Len: Word; Code: Word;
begin
 New(Result, Init);
 with Result.Add^ do
 begin
  PW := Addr(ROM[X]);
  while PW^ <> 0 do
  begin
   if (PW^ = $000E) or (PW^ = $0E00) then //Двухбайтовая система кодов с $0E (Здесь LE и BE).
   begin
   if PW^ = $0E00 then Len:=1 else Len:=0; //Используем переменную Len не по назначению: для проверки Endiannes.
    Str := Str + PC.A + PC.B; // Читаем 2 байта: $000E или $0E00.
    Inc(PW); //Переходим к следующему слову.
    Str := Str + PC.A + PC.B; // Читаем 2 байта: они нам не интересны.
    Inc(PW); //Переходим к следующему слову.
    Str := Str + PC.A + PC.B; // Читаем 2 байта: они нам тоже не интересны.
    Inc(PW); //Переходим к следующему слову.
    Str := Str + PC.A + PC.B; // Читаем 2 байта: добрались до размера. Он показывает сколько байт в коде ещё осталось прочитать.
    Code := PW^; //Запоминаем размер, кодов больше 255 не встречал, но код взял словом (может так правильней).
    Inc(PW); //Переходим к следующему слову.
    if Len=1 then  Len := SwapMe2(Code) else Len := Code; //Сохраняем размер в Len(переводим в LE если нужно).
    X := Length(Str); //
    SetLength(Str, X + Len);    //
    Move(PB^, Str[X + 1], Len); //
    Inc(PB, Len) //Здесь считываем нужное количество байт.
   end else
   begin
    Str := Str + PC.A + PC.B; //Обычное чтение, когда никакие контрольные коды не встретились.
    Inc(PW)
   end
  end
 end
end;


Фрагмент таблицы BE
Код:
000E0000000300020000=[Red1]
000E0000000300020001=[Red2]
000E0000000300020002=[Yellow1]
000E0000000300020003=[Blue1]
000E0000000300020004=[Green1]
000E0000000300020005=[Yellow2]
000E0000000300020006=[Violet]
000E0000000300020007=[Green2]
000E0000000300020008=[Blue2]
000E0000000300020009=[Red4]
000E000000030002000A=[Silver]
000E000000030002000B=[Gold]
000E000000030002000C=[Black]
000E000000030002FFFF=[/C]
000E000100000002FFFF=[But1]
000E0001000100020000=[But2]
000E0001000400020001=[Delay01]
000E00010005000400050000=[01_0039]
000E00010006000200CD=[01_0067]
000E000100070004FF000000=[01_0084]
000E00010008000200CD=[01_0085]
000E00010009000400000000=[01_0090]
000E0001000900040000FF05=[01_0253]
000E00010009000400FFFFFF=[01_1118]
000E0001000D00020500=[01_1464]
000E0001000F0000=[01_1467]
000E00010011000201CD=[01_1468]
000E00010012000400000001=[01_1477]
000E000200000000=[Link]
000E0002000100020001=[Item001]
000E00020002000400000000=[Var0]
000E0002000300060000000000CD=[Count01]
000E00020004000200CD=[A]
000E00020004000201CD=[B]
000E000300010000=[03_1677]
000E00030004000201CD=[Bug01]
000E00010005000400000000=[01_003E]
000E0001001000080000045700002EEF=[StaFF]

000A
ends
0000


Фрагмент таблицы LE
Код:
0E000000020002005A00=[00_001]
0E000000030002000900=[Blue]
0E000000030002000A00=[Red]
0E000000030002000B00=[Green]
0E00000003000200FFFF=[/C]
0E00010000000000={Link}
0E00010001000000={Player1}
0E00010002000000={Player2}
0E00010003000000={Player3}
0E000100050006000000000000CD=[01_014]
0E000100050006000000FFFF00CD={count01}
0E0001000600020002CD={choice01}
0E0001000600020003CD={choice02}
0E0001000600020004CD={choice03}
0E000100070002000100=[01_031]
0E0001001100040014002800=[01_061]
0E000200000002000000={-Zelda0-}
0E00020001000400000000CD={-Eastern Palace1-}
0E00020001000400220000CD={-Lost Woods1-}
0E00020002000400580001CD={-Hammer6-}

0A00
ends
0000


Единственным недостатком подобных плагинов является то, что поле размера в файлах bmg или msbt не изменяется при пересборке, но это не относится к правильному извлечению\вставке текста. )

+ Плагин другого типа, но весьма полезный для завершения этой статьи, написан на основе примера из доки Griever'a.
Хоть тут и не запаковка, но читается в проект не то, что записано в оригинальном файле, а записывается не то, что хранится в тексте проекта... Но, в итоге, вставка оригинального текста 1в1.
P.S. Для пересчёта длины строк, что находится рядом с указателями, используется отдельная утилитка, стандартными средствами круптара это не сделать, т.к. указатели загружаются из текстового файла, а как это внести в плагин разбираться уже не стал.

Пример плагина для кодировки UTF-8 с выравниванием системных кодов, что используется в Зельдах FSAE и MM3D. (Если не выравнивать, плашки с текстом просто закрываются, хотя в OoT3D, при той же кодировке и системе кодов, такой фигни ещё не было)
В таблице продублируем каждый встреченный не выровненный системный код выровненным и наоборот.
Для удобства расчётов и красоты текста в проекте, выровненные коды которые уже есть в оригинале будем читать как не выровненные, ибо нефиг.

Код:
Function GetData(TextStrings: PTextStrings): String; stdcall;
Var
 R: PTextString;
 I, J: integer;
begin
 Result := '';
 I := 1;                              \\ Счётчик записываемых символов
 J := 0;                              \\ Счётчик дополнительных 00
 If TextStrings = NIL then Exit;
 With TextStrings^ do
 begin
  R := Root;
  While R <NIL> Length(Str) then                  \\ Когда счётчик записываемых символов стал длиннее строки
  begin Result := Result + #127 + #0 + #0; break       \\ Пишем результат, добавляем к нему символ конца строки, брякаем цикл.
  end;
   if Byte(Str[I]) = $7F then                      \\ Если записали первый символ системного кода, то
    if (I + J) mod 2 <>0 then                   \\ Если позиция $7F нечет, то
         begin Result := Result + Char(Str[I]) + #0; Inc(I); Inc(J) end   \\ Добавляем 00 и увеличиваем оба счётчика на 1.
    else begin Result := Result + Char(Str[I]); Inc(I) end   \\ Иначе, если чёт, просто записываем символ и инкрементим только счётчик I.
    else begin Result := Result + Char(Str[I]); Inc(I) end;   \\ А если не код $7F, то просто пишем символ и инкрементим счётчик.
   until false                           \\ Всё это продолжается пока цикл не брякнется.
  end;
   R := R^.Next
  end
 end
end;

Function GetStrings(X, Sz: Integer): PTextStrings; stdcall;
var
P: PByte; PC: PChar absolute P;
Code: Byte;
begin
 New(Result, Init);
 with Result.Add^ do
 begin
  P := Addr(ROM[X]);
  while 2+2=4 do                     \\ Покуда не брякнемся
  begin
   if P^ = $7F then                     \\ Если видим первый символ системного кода, то
   begin
    Inc(P);                           \\ Посмотрим кто стоит за ним
    Code := P^;
    Inc(P);
    case Code of
    $00: case P^ of                     \\ Если это ноль
             $00: break;                     \\ А за ним ещё ноль, то это конец строки, выходим не читая.
             else begin                     \\ Если за нулём идёт что-то другое, то ноль мы пропускаем не читая, а $7F и это что-то прочтём.
             Dec(P,2); Str := Str + PC^;
             Inc(P,2); Str := Str + PC^;
             Inc(P)
                  end
          end;
    else begin                        \\ Если не ноль
    Dec(P,2); Str := Str + PC^;            \\ Читаем все оба два.
    Inc(P); Str := Str + PC^;
    Inc(P)
         end
    end;
   end else                           \\ Если не код, то обычное чтение.
    begin
      Str := Str + PC^;
      Inc(P)
    end
  end
 end
end;


Фрагмент таблицы
Код:
41=A
42=B
43=C
44=D
45=E
...
61=a
62=b
63=c
64=d
65=e
...
D090=А
D091=Б
D092=В
D093=Г
D094=Д
D095=Е
...
D0B0=а
D0B1=б
D0B2=в
D0B3=г
D0B4=д
D0B5=е
...
7F0A000100=[A]
7F000A000100=[A+]
7F0A000200=[B]
7F000A000200=[B+]
7F0A000300=[X]
7F000A000300=[X+]
7F0A000400=[Y]
7F000A000400=[Y+]
7F0A000500=[L]
7F000A000500=[L+]
7F0A000600=[R]
7F000A000600=[R+]
7F0A000900=[D-Pad]
7F000A000900=[D-Pad+]
7F0E00=[7F_068]
7F000E00=[7F_068+]
7F11000000=[/C]
7F0011000000=[/C+]
7F11000100=[Red]
7F0011000100=[Red+]
7F11000300=[Blue]
7F0011000300=[Blue+]

7F0100
7F000100
7F0200
7F000200
ends
7F0000


Если сложилось впечатление, что Kruptar подходит исключительно для перевода серии Zelda, то это неверное впечатление...


Последний раз редактировалось: Chime[RUS] (Сб Июл 29, 2017 4:35 pm), всего редактировалось 18 раз(а)
Вернуться к началу
Посмотреть профиль Отправить личное сообщение
Anton



Зарегистрирован: 10.11.2003
Сообщения: 727
Откуда: Киев

СообщениеДобавлено: Вс Апр 03, 2016 6:54 pm    Заголовок сообщения: Ответить с цитатой

Молодец, скоро Круптар-таки обзаведется вменяемым туториалом (да уже, в общем).
Вернуться к началу
Посмотреть профиль Отправить личное сообщение Посетить сайт автора
Показать сообщения:   
Начать новую тему   Ответить на тему    Список форумов shedevr.org.ru -> Утилита Kruptar Часовой пояс: GMT + 3
Страница 1 из 1

 
Перейти:  
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах


Powered by phpBB © 2001, 2005 phpBB Group