Старые Консольные Игры

Объявление

Внимание!
Сайт и форум переехали!

Новый адрес сайта - http://consolgames.ru/
Временный адрес форума - http://consolgames.ru/forum/

Информация о пользователе

Привет, Гость! Войдите или зарегистрируйтесь.


Вы здесь » Старые Консольные Игры » Разрабатываемые проекты » Ever17 - the out of infinity (PC)


Ever17 - the out of infinity (PC)

Сообщений 1 страница 30 из 35

1

Итак....

Перевод ведётся с 4 Февраля 2007 года одним единственным человечком. Однако, ввиду многочисленных "доброжелателей", он тормозится (люди хотят браться за перевод сами но, получив в распоряжение необходимые инструменты, быстро теряют к нему интерес).

Итак, что Я знаю о форматах игры на данный момент и какие инструменты существуют:

DAT (LNK) - обычный пак-стрим формат. Имена файлов не могут превышать 24 символа (включая расширение). Шифрование - не применяется. Формат реверс-инженирован на 100%.

WAF - обычный RIFF WAVE MS ADPCM с модифицированным заголовком. Формат реверс-инженирован на 80%.

lnd - WAF-файлы, пожатые по алгоритму LZ или RLE (пока не разобраны).

Игровой движок понимает как собственный формат графики (CPS\PRT), так и стандартный BMP-24.

CPS - шифрованный LZ+RLE-архив, содержащий графический файл в формате PRT.

PRT - модификация BMP-24, а НЕ RAW, как думает Serke. :-P

PRT без альфа канала имеют идентичную с BMP структуру потока.

PRT *с альфа каналом* состоят из двух частей:

1. потока BMP-24 с интерливингом в 3 байта $0 для каждой скан-линии.
2. потока BMP-8 Grayscale (перевёрнутый), в котором и содержатся данные о транспаренции.

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

Более-менее рабочий SDK для движка KID Engine (именно SDK, а не просто унпакер) - AnimED (софтинка собственного производства ^_^). http://www.dsp2003.narod.ru/animed.htm. Сорсы для Delphi 7 в комплекте. Предупреждаю, программа уже устарела, но новая версия ещё не зарелизена ввиду немного затянувшейся перекройки ядра.

Формат CPS был разобран Serke и HoRRoR'ом.

Жду сорсов на Delphi чтобы собрать из всего этого нормальный SDK для игры.

0

2

WinKi написал(а):

PRT - модификация BMP-24, а НЕ RAW, как думает Serke. :-P

BMP самый обыконвенный. Конвертируется в BMP с помощью TDIB (DelphiX) парой строк :P

WinKi написал(а):

Жду сорсов на Delphi чтобы собрать из всего этого нормальный SDK для игры.

Модуль пакера.

Function Compress(var Buf: Pointer {Пакуемый поток}; Size: Integer {Размер потока}; fBLK,fLZ,fRLE: Boolean {Применяемые методы}; Progress: TProgrProc = NIL {Процедура прогресса (необязательно)}): Integer;
Возвращает размер запокованного потока.

Пример применения:

Код:
var F,WF: File; Buf: Pointer;  Size: Integer;
begin
  If not FileExists(ParamStr(1)) Then Exit;
  AssignFile(F, ParamStr(1));
  Reset(F,1);
  If ParamCount>1 Then AssignFile(WF,ParamStr(2))
  else AssignFile(WF,ParamStr(1)+'.qpk');
  GetMem(Buf,FileSize(F));
  Rewrite(WF,1);
  Size:=FileSize(F);
  BlockRead(F,Buf^,Size);
  BlockWrite(WF,QPK,4);
  Seek(WF,4);
  BlockWrite(WF, Size,4);
  Seek(WF,8);
  BlockWrite(WF,Buf^,Compress(Buf,Size,True,True,True));
  FreeMem(Buf);
  CloseFile(F);
  CloseFile(WF);
end.

0

3

CPS - шифрованный LZ+RLE-архив

Modified LZSS+RLE+BLK.

Я вот думаю, как быть. Анпакер, расшифровщик и шифровщик - на C, пакер же на Delphi. Что-то нужно портировать.

0

4

Serke написал(а):

Я вот думаю, как быть. Анпакер, расшифровщик и шифровщик - на C, пакер же на Delphi. Что-то нужно портировать.

DLL? :)

0

5

Кстати, WinKi, слышал, что у вас там разногласия насчёт того, криптовать данные или нет. Так вот, я за криптовку, объясню почему:
1. Самая главная причина - ператы. Уж поверь, твой перевод с довольно большой вероятностью может оказаться на прилавках под лейблом какой-нибудь конторы вроде Вектор. Так пусть хоть в самой игре логотипы и т.п. останутся твоими, а не ператскими.
2. Есть немало гениев, которые могут воспользоваться открытостью данных и русефикализировать игру на своё вкус. Почитай, например, тему про альтернативный перевод FF8 на этом форуме.
3. В игру сможет подписать своё имя или изменить авторов, пустив по сети свою версию патча, кто угодно.

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

0

6

HoRRoR, проблема в соглашениях о вызовах. В Visual C, если верить Касперски, соглашение PASCAL определено как stdcall. Впрочем, тот же Касперски уверяет в том, что борландовский C отсебятину не порет. Проверю.

Благодарю за поддержку, HoRRoR. Я, пожалуй, тоже отпишусь...

Позволю себе напомнить тебе, WinKi, что ты не просто переводчик, ты лидер проекта. Лидер проекта, да простят меня здесь присутствующие столь вольную интерпретацию, это человек, который должен решать проблемы организационного плана, утрясать конфликты внутри коллектива, прислушиваться к самым разным мнениям и выпрыгивать из штанов, чтобы внутренние разногласия не привели к коллапсу проекта. Ты же ставишь ультиматумы, смысл которых понятен одному тебе и порождаешь конфликт, ставящий под угрозу благополучие проекта, в который ты, рискну предположить, вложил уйму сил и на который потратил уйму времени.

Еще раз тебе говорю: я не против того, чтобы информация была свободна. Ни я, ни HoRRoR ничего утаивать не собираемся - ты получишь полные исходники, экзешники, dll'ки и разъяснения. Однако я настаиваю на том, чтобы файлы, которые изначально были зашифрованы и запакованы, после модификации были зашифрованы и запакованы взад, а также на том, чтобы исходники и бинарники выдавались тем, кто в них нуждается, а не выбрасывались в паблик. Вьюер и экстрактор графики (KID Tools который) доступен любому желающему, поэтому твой аргумент о том, что пользователю стоит дать возможность вытаскивать графику и любоваться на картинки считаю несостоятельным - такая возможность у него уже есть. Если найдется кто-то, кто посчитает, что твой перевод далек от идеала и захочет заняться переводом самолично - великолепно, буду только рад и с удовольствием ему помогу. Но только после того, как удостоверюсь в серьезности его намерений и в том, что перевод действительно будет качественным. Если кто-либо изъявит желание перевести другие игры серии Infinity с японского, буду первым, кто протянет руку помощи (а для этого мне снова нужно будет вооружаться дизассемблером и отладчиком, поскольку, как ты помнишь, движок использует минимум две функции распаковки). Пойми же, наконец: я за доступность информации, но только если она используется во благо, а не во вред.

Выражаю надежду на то, что я не впустую сотрясал воздух и что мои доводы все-таки будут услышаны.

Отредактировано Serke (2007-11-27 05:47:01)

0

7

прям закрытый форум разработчиков  :D

0

8

Эхх... ладушки. Все конфликты исчерпаны, ибо были основаны на ложной информации.

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

Можно ли перекроить функции паковки\распаковки, чтобы на выходе получались не указатели, а данные в виде TStream (прекрасно понимаю, что это медленней, чем работа с памятью и указателями, но иначе просто не смогу полноценно встроить код в AnimED)?

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

Как говорится, "лидер" туп, и он этого не скрывает. :(

0

9

WinKi написал(а):

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

Да я и сам только учусь ;)

WinKi написал(а):

Можно ли перекроить функции паковки\распаковки, чтобы на выходе получались не указатели, а данные в виде TStream (прекрасно понимаю, что это медленней, чем работа с памятью и указателями, но иначе просто не смогу полноценно встроить код в AnimED)?

Как вариант, можно сделать так:

Код:
Var Buf: Pointer;
.......
Getmem(Buf, Stream.Size);
Stream.Read(Buf^, Stream.Size);
Stream.Position := 0; 
Stream.Size:=Compress(Buf, Stream.Size, True,True,True);
Stream.Write(Buf^, Stream.Size);
FreeMem(Buf);

А если встраивать в функцию, то так:

Код:
Function Compress(var Stream: TStream; Size: Integer; fBLK,fLZ,fRLE: Boolean; Progress: TProgrProc = NIL): Integer;
var Buf,WBuf: Pointer; TB,TWB,B,WB: ^Byte;
Sz,Num,WSz,M,Count,Vr,n,PSize: Integer; I: ^Integer; tFlag: Boolean;
ReadCount: Integer;
begin
  Getmem(Buf, Stream.Size);
  Stream.Read(Buf^, Stream.Size);
  GetMem(WBuf,Size+(Size div $800)+4);
  B:=Addr(Buf^);
  WB:=Addr(WBuf^);
  Sz:=Integer(B);
  WSz:=Integer(WB);
  ReadCount:=0;
  While ...
  ...
  end;
  Result:=Integer(WB)-WSz;
  FreeMem(Buf);
  Stream.Position := 0; 
  Stream.Size := Result;
  Stream.Write(WBuf^, Stream.Size);
  FreeMem(WBuf);
end;

Только сперва добавь в uses Classes.

Код:
uses
  Windows, SysUtils, Classes;

0

10

Да я и сам только учусь

Ну прямо вечер откровений. Чувствую пятой точкой, что настала моя очередь каяться, поэтому попытаюсь перевести разговор на обсуждение технических деталей. Авось проканает. =)

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

Итак, что касается PRT-заголовка. Цитирую WinKi:

Это текущая версия имплементации PRT-заголовка. Дальше (пока) не разобрал.

TPRT = record
PRTHeader : array[1..6] of char; // 'PRT' + $0 + 'f' + $0
Bitdepth : word; // Bits per pixel
Unknown_0 : array[1..4] of char; // '$' + $0 + '$' + $0
Width : word; // Image Width
Height : word; // Image Height
UsesAlpha : word; // if 0x00, the 18 other bytes is 0x00.
Dummy_18 : array[1..18] of byte; // ???
end;

К сожалению, это частный случай. Массив Dummy_18 может быть и ненулевым при значении UsesAlpha = FALSE, а Unknown_0 может иметь самые разные значения. Вообще, Unknown_0 это два WORD'овых поля, второе из которых содержит смещение относительно начала PRT-файла. Первое, думаю, тоже содержит смещение, но пока это только предположение.

Пока все. Высплюсь и возмусь за дизассемблирование.

EDIT:

Массив Dummy_18 может быть и ненулевым при значении UsesAlpha = TRUE

Пардон. При значении FALSE, конечно же. Исправил.

Отредактировано Serke (2007-11-29 14:58:53)

0

11

Зря исправлял. Всё было правильно. При значении FALSE (0x0000) следующие 18 байтов будут равны 0x00. Насчёт Unknown_0 и того, что я посчитал частью заголовка - это могут быть указатели на тип данных, используемых в изображении\альфа-канале (если он присутствует).

0

12

HoRRoR, после модификации функции... гм... она перестала работать - просто копирует первые 5 килобайт файла, а затем всё...

Эхх, лучше сделаю промежуточную функцию по твоему примеру... ^_^

0

13

WinKi написал(а):

HoRRoR, после модификации функции... гм... она перестала работать - просто копирует первые 5 килобайт файла, а затем всё...

Сорри, ошибся слегка...
Третью снизу строку исправь на Stream.Write(WBuf^, Stream.Size);
Исправил пост сверху...

0

14

"Я окончательно перестал что-либо понимать." (с) Serke

Это то, во что превратился код после нескольких манипуляций в Delphi.

Это код кнопки:

Код:
procedure TMainForm.SB11Click(Sender: TObject);
var InputStream : TFileStream; OutputStream : TStream;
begin
 if OpenDialogFile.Execute then
  begin
   InputStream := TFileStream.Create(OpenDialogFile.FileName,fmOpenRead);
   OutputStream := TFileStream.Create(OpenDialogFile.FileName+'.packed',fmCreate);
   OutputStream.CopyFrom(InputStream,InputStream.Size);
   FreeAndNil(InputStream);
   OutputStream.Seek(0,soBeginning);
   CPS_Stream(OutputStream, coCompressOnly);
   FreeAndNil(OutputStream);
  end;
end;

Результат работы кода с TStream весьма странный... файл раздувает в размере. :(

Отредактировано WinKi (2007-11-29 18:19:02)

0

15

WinKi написал(а):

Результат работы кода с TStream весьма странный... файл раздувает в размере.

Вся фишка в том, что Size в функции распаковки - размер распакованного файла, он есть в заголовке. Если он неизвестен, из архива его взять невозможно. Кстати добавь в конец Result:=Size; либо же преврати функцию в процедуру.

Вот примерно какой должен быть код:

Код:
procedure CPS_Stream(var Stream : TStream; Operation : byte; Size: Integer = NIL);
var Buf: Pointer;
begin
 If (Operation=CPS_Decompress) and (Size=NIL) Then Exit;
 Getmem(Buf, Stream.Size);
 Stream.Read(Buf^, Stream.Size);
 Stream.Position := 0;
 case Operation of
  coDecompress       : Stream.Size := CPS_Decompress(Buf, Size);
  coCompressOnly     : Stream.Size := CPS_Compress(Buf, Stream.Size, True,True,True);
  coCompressEncode   : ;
 end;
 Stream.Write(Buf^, Stream.Size);
 FreeMem(Buf);
end;

0

16

Таааак.... теперь всё совсем весело -- оказывается получившийся код делает следующее - копирует часть исходного потока, а затем присоединяет к нему запакованный (похоже, что Stream в каком-то месте надо перематывать на начало функцией Stream.Seek(0,soBeginning); )... что за головная боль... :(

0

17

Попробуй так:

Код:
procedure CPS_Stream(var Stream : TStream; Operation : byte);
var Buf: Pointer;
begin
 Getmem(Buf, Stream.Size);
 Stream.Read(Buf^, Stream.Size);
 Stream.Position := 0;
 case Operation of
  coDecompress       : Stream.Size := CPS_Decompress(Buf, Stream.Size);
  coCompressOnly     : Stream.Size := CPS_Compress(Buf, Stream.Size, True,True,True);
  coCompressEncode   : ;
 end;
 Stream.Position := 0;
{Stream.Seek(0,soBeginning);} // раскомментишь, если не сработает...
 Stream.Write(Buf^, Stream.Size);
 FreeMem(Buf);
end;

0

18

Сработало! ^_^

В начале запакованного файла, перед PRT-заголовком, стоит байт 0x08. Если так и должно быть, то всё работает. :)

Осталось сделать функцию шифрования и генератор CPS-заголовка. :-D

0

19

WinKi

В начале запакованного файла, перед PRT-заголовком, стоит байт 0x08. Если так и должно быть, то всё работает.

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

Зря исправлял. Всё было правильно. При значении FALSE (0x0000) следующие 18 байтов будут равны 0x00.

Если бы. Взгляни на PRT-заголовок файла mask.cps

0

20

Насчёт mask.cps:

0. Это 256-цветный (Grayscale) PRT. ^_^
1. После байта UsesAlpha идёт лишь (!) три (!) пустых байта, а затем начинается палитра.

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

И ещё, похоже UsesAlpha - это longword (4 байта):

* в 8-битках (выдрал из скринсейверов) без альфа-канала после него сразу идёт стрим картинки.
* в 24-битовых без альфа-канала перед данными вставляются "лишние" 16 байт (0x00).

Отсюда вывод -- размер заголовка PRT варьируется. :)

EDIT: Новая версия PRT-имплементации:

Код:
TPRT = record
 Header : array[1..4] of char; //PRT+#0
 HeaderType : word; //101 (0x65) - 8-bit or w\o alpha header, 102 (0x66) - 24-bit or w\ alpha header
 Bitdepth : word; // Bits per pixel
 Offset1 : word; // PRT header size \ palette beginning offset
 Offset2 : word; // Picture data beginning offset
 Width : word; // Image Width
 Height : word; // Image Height
 UsesAlpha : longword; // Uses alpha or not (0x0000 - false, 0x0001 - true)
// Optional alpha-related data (not writed if HeaderType < 102)
 Unknown1 : longword; // ???
 Unknown2 : longword; // ???
 Unknown3 : longword; // ???
 Unknown4 : longword; // ???
end;

Цель близко. ^_^

EDIT2: Не трудно догадаться, что для 24-битных файлов значения Offset1 и Offset2 идентичны, т.к. в них отсутствует палитра. ^_~

EDIT3: Я буду долго ржать, если окажется, что Unknown-ы - это:

а) Координаты для рендеринга картинки
б) количество пикселей #000000, на которые надо canvas-нуть картинку по вертикали и горизонтали, чтобы получилось изображение 800x600.

EDIT4: Появилась мысль, что это таки размер альфы и координаты её наложения относительно главной картинки...  ^_^

Отредактировано WinKi (2007-11-30 13:27:26)

0

21

Ну так в чём проблема? Изменяй значения и смотри на результат в игре...

0

22

Ржунемагу....

Код:
 TPRT = record
  Header     : array[1..4] of char; //PRT+#0
  HeaderType : word;                //101 (0x65) - 8-bit or w\o alpha header, 102 (0x66) - 24-bit or w\ alpha header
  Bitdepth   : word;                // Bits per pixel
  Offset1    : word;                // PRT header size \ palette beginning offset
  Offset2    : word;                // Picture data beginning offset
  Width      : word;                // Image Width
  Height     : word;                // Image Height
  UsesAlpha  : longword;            // Uses alpha or not (0x0000 - false, 0x0001 - true)
// Optional canvas data (not writed if HeaderType < 102)
  X          : longword;            // X render coordinate
  Y          : longword;            // Y render coordinate
  RealWidth  : longword;            // Real Image Width
  RealHeight : longword;            // Real Image Height
 end;

Заголовок разобран полностью. Догадка про рендеринг по разрешению 800 на 600 подтвердилась. ^_^ Осталось только разобрать способ записи картинки\альфы и дело в шляпе - будет конвертер PRT\BMP-32\TGA.

Всё, снежный некоид утопал пытать Delphi. :rofl:

Отредактировано WinKi (2007-12-01 08:28:17)

0

23

Прошу прощения за молчание... Перады скардили и зарелизили наисвежайшую версию IDA Pro Advanced (5.2) и тот самый Hex-Rays Decompiler который, судя по тому, что о нем пишут, представляет собой девятое чудо света (восьмым является сама IDA) и работа встала. :)

Итак, что происходит с файлом mask.cps после его расшифровки и распаковки:

1. Вызывается некая функция (назовем ее sub_X). Вот прототип этой функции:

int sub_X( int aIndex, int aWidth, int aHeight, int aBitCount).

Где aWidth и aHeight соответственно – ширина и высота картинки. Эти параметры берутся из заголовка PRT-файла. А вот aBitCount (размерность изображения в битах) передается функции явно, в виде константы. Только не спрашивай меня, почему он не берется из заголовка, как aWidth и aHeight. =)

В теле этой функции происходит создание битмапки (вызываются API-функции CreateCompatibleDC + CreateDIBSection).

Из файла WIN32.HLP: The CreateDIBSection function creates a device-independent bitmap (DIB) that applications can write to directly. The function gives you a pointer to the location of the bitmap's bit values. You can supply a handle to a file mapping object that the function will use to create the bitmap, or you can let the operating system allocate the memory for the bitmap.

HBITMAP CreateDIBSection(

    HDC hdc, // handle to device context
    CONST BITMAPINFO *pbmi, // pointer to structure containing bitmap size, format, and color data
    UINT iUsage, // color data type indicator: RGB values or palette indices
    VOID *ppvBits, // pointer to variable to receive a pointer to the bitmap's bit values
    HANDLE hSection, // optional handle to a file mapping object
    DWORD dwOffset // offset to the bitmap bit values within the file mapping object
   );

The BITMAPINFO structure defines the dimensions and color information for a Windows device-independent bitmap (DIB).

typedef struct tagBITMAPINFO { // bmi 
   BITMAPINFOHEADER bmiHeader;
   RGBQUAD          bmiColors[1];
} BITMAPINFO;

Members:

bmiHeader

Specifies a BITMAPINFOHEADER structure that contains information about the dimensions and color format of a DIB.

bmiColors

Specifies an array of RGBQUAD or doubleword data types that define the colors in the bitmap.

Что касается структуры BITMAPINFOHEADER, входящей в состав структуры BITMAPINFO:

The BITMAPINFOHEADER structure contains information about the dimensions and color format of a device-independent bitmap (DIB).

typedef struct tagBITMAPINFOHEADER{ // bmih 
   DWORD  biSize;
   LONG   biWidth;
   LONG   biHeight;
   WORD   biPlanes;
   WORD   biBitCount
   DWORD  biCompression;
   DWORD  biSizeImage;
   LONG   biXPelsPerMeter;
   LONG   biYPelsPerMeter;
   DWORD  biClrUsed;
   DWORD  biClrImportant;
} BITMAPINFOHEADER;

Более подробно читай в MSDN. Поля структуры tagBITMAPINFOHEADER функцией sub_X заполняются следующим образом:

biSize     = sizeof( tagBITMAPINFOHEADER ); // размер самой структуры
biWidth    = aWidth;
biHeigth   = aHeight;
biPlanes   = (WORD)1;
biBitCount = (WORD)aBitCount;
biCompression = BI_RGB; // An uncompressed format.
biSizeImage = 0; // This may be set to 0 for BI_RGB bitmaps.
biXPelsPerMeter = 0;
biYPelsPerMeter = 0;
biClrUsed = 0;
biClrImportant = 0;

^^ Практически готовый BMP-хэдер.

CreateDIBSection вызывается следующим образом:
CreateDIBSection( CreateCompatibleDC(0), указатель_на_экземпляр_структуры_tagBITMAPINFO, 0, &pvBits, 0, 0 );

2. To be continued ^__^

Отредактировано Serke (2007-12-04 15:46:44)

0

24

Хииииии.... что называется, прорвало! ^_^

Ну, и я в долгу не останусь - сегодня выложил наисвежайшую версию своего монстрика:

http://www.dsp2003.narod.ru/download/an … _alpha.exe

Уже умеет открывать альфаканальные PRT... правда, с самой альфой (пока) не дружит и немножко коцает картинку при открытии (чувствую, что где-то в определении размера потока напортачил ^_^').

Нетерпеливо жду продолжения банкета. :lol:

0

25

Хотел продолжить банкет, но передумал. Псевдокод функции sub_X, выдаваемый хэксрейзом, содержит ошибки, придется декомпилять дедовским способом - ручками и головой. Длина потока (если я правильно понял, под потоком ты подразумеваешь область памяти с пиксельными данными изображения, смещение на начало которых содержится в поле offset2 заголовка) вычисляется довольно странным образом.

P.S. Не завидую я тем, кто раскошелился на $2300 за хексрейс. Ох, не завидую...

0

26

Товарищи (господа, сеньоры, доны – нужное подчеркнуть) есть ли среди вас знатоки C и Паскаля? Боюсь, если я вывалю на WinKi с нетерпением ожидаемый им продукт реверсивной инженерии, его хватит удар. :)

А впрочем, я его все же вывалю...

Код:
#include <windows.h>
#include <wingdi.h>

struct PRTHeader {
    char ID[4];
    WORD HeaderType;
    WORD BitDepth;
    WORD Offset1;
    WORD Offset2;
    WORD Width;
    WORD Height;
    DWORD UsesAlpha;
};

struct myBITMAPINFO {
    struct BITMAPINFOHEADER bmiHeader;
    struct RGBQUAD bmiColors[256];
};

struct PRTStruct {
    int Width1;   
    int Height1;
    int BitCount;
    struct myBITMAPINFO bmi;
    LPVOID pvBits;
    int field_438;
    LPVOID lpMem;
    int ScanlineLen;
    HBITMAP hBitmap;
    HDC hDC;
    int NumOfBytes;
    int field_450;
    int Width2;
    int Height2;
    int field_45C;
    int field_460;
    int field_464;
    int Width3;
    int Height3;
};

// Некая глобальная 32-битная signed переменная:
int dword_45E630;

// Глобальный массив указателей на структуры типа PRTStruct:
struct PRTStruct *pPRTStructTbl[ ??? ];

// Прототипы функций:
int PRTStructInit( int, int, int, int );
int sub_42BFD0( int );

////////////////////////////////////////////////

int PRTStructInit( int aIndex, int aWidth, int aHeight, int aBitCount )
{
    int PixelDataLen;
    
    if( pPRTStructTbl[ aIndex ] )
    {
        if( ( (pPRTStructTbl[ aIndex ] -> Width1) == aWidth ) &&
            ( (pPRTStructTbl[ aIndex ] -> Height1) == aHeight ) &&
            ( (pPRTStructTbl[ aIndex ] -> BitCount) == aBitCount ) )
        {
            sub_42D6F0( aIndex, 0, 0, 0, 0 );
            return 1;
        }
        sub_42BFD0( aIndex );
    }
    
    pPRTStructTbl[ aIndex ] = (struct PRTStruct *)HeapAlloc( GetProcessHeap(), 
                                                             HEAP_ZERO_MEMORY,
                                                             sizeof( struct PRTStruct ) );
    pPRTStructTbl[ aIndex ] -> field_460 = 0;
    pPRTStructTbl[ aIndex ] -> field_464 = 0;
    pPRTStructTbl[ aIndex ] -> Width3 = aWidth;
    pPRTStructTbl[ aIndex ] -> Height3 = aHeight;
    
    switch( aBitCount )
    {
        case 4:
            pPRTStructTbl[ aIndex ] -> ScanlineLen = ( ( aWidth + 7 ) >> 3 ) << 2;
            break;
        case 8:
            pPRTStructTbl[ aIndex ] -> ScanlineLen = ( ( aWidth + 3 ) >> 2 ) << 2;
            break;
        case 16:
            pPRTStructTbl[ aIndex ] -> ScanlineLen = ( ( aWidth * 2 + 3 ) >> 2 ) << 2;
            break;
        case 24:
            pPRTStructTbl[ aIndex ] -> ScanlineLen = ( ( aWidth * 3 + 3 ) >> 2 ) << 2;
            break;
        case 32:
            pPRTStructTbl[ aIndex ] -> ScanlineLen = aWidth * 4;
            break;
        default:
            break;
    }
    
    PixelDataLen = ( pPRTStructTbl[ aIndex ] -> ScanlineLen ) * aHeight;
    dword_45E630 += PixelDataLen;
    pPRTStructTbl[ aIndex ] -> NumOfBytes = PixelDataLen;
    
    if( aHeight > 0 )
        pPRTStructTbl[ aIndex ] -> ScanlineLen = - ( pPRTStructTbl[ aIndex ] -> ScanlineLen );
        
    pPRTStructTbl[ aIndex ] -> Width1 = aWidth;
    pPRTStructTbl[ aIndex ] -> Height1 = aHeight;
    pPRTStructTbl[ aIndex ] -> BitCount = aBitCount;
    
    pPRTStructTbl[ aIndex ] -> bmi.bmiHeader.biSize = sizeof( struct tagBITMAPINFOHEADER );
    pPRTStructTbl[ aIndex ] -> bmi.bmiHeader.biWidth = aWidth;
    pPRTStructTbl[ aIndex ] -> bmi.bmiHeader.biHeight = aHeight;
    pPRTStructTbl[ aIndex ] -> bmi.bmiHeader.biPlanes = 1;
    pPRTStructTbl[ aIndex ] -> bmi.bmiHeader.biBitCount = aBitCount;
    pPRTStructTbl[ aIndex ] -> bmi.bmiHeader.biCompression = BI_RGB;
    pPRTStructTbl[ aIndex ] -> bmi.bmiHeader.biSizeImage = 0;
    pPRTStructTbl[ aIndex ] -> bmi.bmiHeader.biXPelsPerMeter = 0;
    pPRTStructTbl[ aIndex ] -> bmi.bmiHeader.biYPelsPerMeter = 0;
    pPRTStructTbl[ aIndex ] -> bmi.bmiHeader.biClrUsed = 0;
    pPRTStructTbl[ aIndex ] -> bmi.bmiHeader.biClrImportant = 0;
    
    pPRTStructTbl[ aIndex ] -> hDC = CreateCompatibleDC( 0 );
    pPRTStructTbl[ aIndex ] -> hBitmap = CreateDIBSection( pPRTStructTbl[ aIndex ] -> hDC,
                                                           ( struct tagBITMAPINFO *)&( pPRTStructTbl[ aIndex ] -> bmi ),
                                                           DIB_RGB_COLORS,
                                                           ( LPVOID *)&( pPRTStructTbl[ aIndex ] -> pvBits ),
                                                           NULL,
                                                           0 );
    GetLastError();
    SelectObject( pPRTStructTbl[ aIndex ] -> hDC, pPRTStructTbl[ aIndex ] -> hBitmap );

    pPRTStructTbl[ aIndex ] -> field_438 = ( pPRTStructTbl[ aIndex ] -> pvBits ) - ( ( pPRTStructTbl[ aIndex ] -> ScanlineLen ) * ( aHeight - 1 ) );
    pPRTStructTbl[ aIndex ] -> field_450 = 1;
    pPRTStructTbl[ aIndex ] -> field_45C = 1;
    pPRTStructTbl[ aIndex ] -> Width2 = aWidth;
    pPRTStructTbl[ aIndex ] -> Height2 = aHeight;
    
    sub_42D6F0( aIndex, 0, 0, 0, 0 );
    
    return 1;
}    

////////////////////////////////////////////////

int sub_42BFD0( int aIndex )
{
    if( pPRTStructTbl[ aIndex ] )
    {
        DeleteDC( pPRTStructTbl[ aIndex ] -> hDC );
        DeleteObject( pPRTStructTbl[ aIndex ] -> hBitmap );
        dword_45E630 -= pPRTStructTbl[ aIndex ] -> NumOfBytes;
        if( pPRTStructTbl[ aIndex ] -> lpMem )
        {
            HeapFree( GetProcessHeap(), 0, pPRTStructTbl[ aIndex ] -> lpMem );
            pPRTStructTbl[ aIndex ] -> lpMem = NULL;
        }
        if( pPRTStructTbl[ aIndex ] )
            HeapFree( GetProcessHeap(), 0, pPRTStructTbl[ aIndex ] );
        pPRTStructTbl[ aIndex ] = NULL;
    }
    return 1;
}

// Код, получающий управление после загрузки, расшифровки и распаковки
// файла mask.cps
//
// pPRTImage = переменная-указатель на имидж PRT-файла mask.cps в памяти
//
// Итак, собственно код:

    // ...
    PRTStructInit( Index, PRTHeader.Width, PRTHeader.Height, 8 );

    memcpy( pPRTStructTbl[ Index ] -> pvBits, 
            &pPRTMemImage[ PRTHeader.Offset2 ],
            - pPRTStructTbl[ Index ] -> ScanlineLength * PRTHeader.Height );

    if( pPRTMemImage )
        HeapFree( GetProcessHeap(), 0, pPRTMemImage );

    return 1;
}

Отредактировано Serke (2007-12-08 06:01:08)

0

27

Удар не хватил, зато многие подозрения подтвердились. ^_^ То, что код преобразует картинку по скан-линиям, понятно - идёт "рендеринг в памяти" на поле, размерностью PRTHeader.Width на PRTHeader.Height...

А вот что там, простите, делают Width3 и Height3 .... не хотите ли сказать, что у альфы собственные размеры? :)

0

28

Вчера и сегодня корпел над универсальной функцией, что смогла бы:

1. Преобразовать картинку любой битовой величины в 32 бита
2. Присоединяла к потоку альфа-канал

Благодаря тебе, Serke, а также ребятам из irc://irc.ru/delphi, её удалось наконец дописать до работающего прототипа. ^_^

На входе процедура требует следующие параметры:

InputStream - битовый поток основного изображения
AlphaStream - битовый поток альфа-картинки
OutputStream - поток, в который сохраняется 32-битная картинка
Width - ширина картинки
Height - высота картинки
BitDepth - битовое разрешение
Palette - packed record-массив палитры картинки в ARGB. Если нет, по умолчанию ставлю функцию, генерирующую GrayScale-палитру - GrayScalePalette.

Код:
unit AnimED_Graphics_Extended;

interface

uses Classes, Sysutils{, AnimED_Math}; // Математический модуль в данном примере не нужен :)

{...}

{ palette table }
 TARGB = packed record
  B : byte; // Blue
  G : byte; // Green
  R : byte; // Red
  A : byte; // Alpha (partial transparency color)
 end;

 TPalette = packed record
  Palette : array[0..255] of TARGB;
 end;

{...}

function GrayscalePalette : TPalette;

procedure to32(InputStream, AlphaStream, OutputStream : TStream; Width, Height : integer; BitDepth : byte; Palette : TPalette);

implementation

{...}

{ Converts to BMP\TGA-compatible 32-bit RAW stream \ combines with external alpha }
procedure to32(InputStream, AlphaStream, OutputStream : TStream; Width, Height : integer; BitDepth : byte; Palette : TPalette);
var i, j, l, m, n,
    counter, acounter,
    ScanlineLen, aScanlineLen,
    InputStreamSize, AlphaStreamSize : integer;
    Gap, aGap, k : byte; // Gap is a number of gapped bytes
    TW : word;
    ARGB : TARGB;
begin
 k := 0;
 m := 0;
 n := 0;

 counter := 0;
 acounter := 0;

 InputStream.Seek(0,soBeginning);
 OutputStream.Seek(0,soBeginning);

{ Note: alpha is ALWAYS stored as 8-bit image ONLY and must be at the same sizes
  as original image (WxH), otherwise an error will occur }
 if AlphaStream <> nil then
  begin
   AlphaStream.Seek(0,soBeginning);
   aScanlineLen := ((Width + 3) shr 2) shl 2;
   aGap := aScanlineLen - Width;
   AlphaStreamSize := aScanLineLen * Height;
  end;

 case BitDepth of
  1 : begin
       for i := 1 to InputStream.Size do
        begin
         InputStream.Read(j,1);
         for k := 8 downto 1 do
          begin
           l := j shr k-1;
           l := l and $1;
           ARGB := Palette.Palette[l];
           if AlphaStream <> nil then
            begin
             inc(m);
             if m >= 3 then
              begin
               m := 0;
               AlphaStream.Read(ARGB.A,1);
               inc(acounter);
               if acounter = Width then
                begin
                 AlphaStream.Seek(aGap,soCurrent);
                 acounter := 0;
                end;
              end;
            end;
           OutputStream.Write(ARGB,4);
          end;
        end;
      end;
  4 : begin
       ScanlineLen := ((Width + 7) shr 3) shl 2;
       Gap := ScanlineLen - Width div 2;
       InputStreamSize := ScanLineLen * Height;
       for i := 1 to InputStreamSize do
        try
         InputStream.Read(j,1);
         inc(counter);
         if counter = (Width div 2) then
          begin
           InputStream.Seek(Gap,soCurrent);
           counter := 0;
          end;
         for l := 1 to 2 do
          begin
           case l of
            1 : k := j and $f;
            2 : k := j shr 4;
           end;
           ARGB := Palette.Palette[k];
           if AlphaStream <> nil then
            begin
             AlphaStream.Read(ARGB.A,1);
             inc(acounter);
             if acounter = Width then
              begin
               AlphaStream.Seek(aGap,soCurrent);
               acounter := 0;
              end;
            end;
           OutputStream.Write(ARGB,4);
          end;
        except
        end;
      end;
  8 : begin
       ScanlineLen := ((Width + 3) shr 2) shl 2;
       Gap := ScanlineLen - Width;
       InputStreamSize := ScanLineLen * Height;
       for i := 1 to InputStreamSize do
        try
         InputStream.Read(j,1);
         inc(counter);
         if counter = Width then
          begin
           InputStream.Seek(Gap,soCurrent);
           counter := 0;
          end;
         ARGB := Palette.Palette[j];
       { Replaces original alpha value with alpha channel }
         if AlphaStream <> nil then
          begin
           AlphaStream.Read(ARGB.A,1);
           inc(acounter);
           if acounter = Width then
            begin
             AlphaStream.Seek(aGap,soCurrent);
             acounter := 0;
            end;
          end;
         OutputStream.Write(ARGB,4);
        except
        end;
      end;
  16: begin
       ScanlineLen := ((Width * 2 + 3) shr 2) shl 2;
       Gap := ScanlineLen - Width * 2;
       InputStreamSize := ScanLineLen * Height;
       for i := 1 to InputStreamSize div 2 do
        begin
         InputStream.Read(tw,2);
         inc(counter,2);
         if counter = (Width * 2) then
          begin
           InputStream.Seek(Gap,soCurrent);
           counter := 0;
          end;
         ARGB.A := tw shr 15;
         ARGB.R := (tw shr 10) and $1F;
         ARGB.G := (tw shr 5) and $1F;
         ARGB.B := tw and $1F;
       { Replaces original alpha value with alpha channel }
         if AlphaStream <> nil then
          begin
           AlphaStream.Read(ARGB.A,1);
           inc(acounter);
           if acounter = Width then
            begin
             AlphaStream.Seek(aGap,soCurrent);
             acounter := 0;
            end;
          end;
         OutputStream.Write(ARGB,4);
        end;
      end;
  24: begin
       ScanlineLen := ((Width * 3 + 3) shr 2) shl 2;
       Gap := ScanlineLen - Width * 3;
       InputStreamSize := ScanLineLen * Height;
       for i := 1 to (InputStreamSize div 3) do
        begin
         InputStream.Read(ARGB.B,1);
         InputStream.Read(ARGB.G,1);
         InputStream.Read(ARGB.R,1);
         inc(counter,3);
         if counter = (Width * 3) then
          begin
           InputStream.Seek(Gap,soCurrent);
           counter := 0;
          end;
         ARGB.A := 0;
       { Replaces original alpha value with alpha channel }
         if AlphaStream <> nil then
          begin
           AlphaStream.Read(ARGB.A,1);
           inc(acounter);
           if acounter = Width then
            begin
             AlphaStream.Seek(aGap,soCurrent);
             acounter := 0;
            end;
          end;
         OutputStream.Write(ARGB,4);
        end;
      end;
  32: begin
       ScanlineLen := Width * 4;
       InputStreamSize := ScanlineLen * Height;
       for i := 1 to (InputStreamSize div 4) do
        begin
         InputStream.Read(ARGB,4);
       { Replaces original alpha channel with new alpha channel }
         if AlphaStream <> nil then
          begin
           AlphaStream.Read(ARGB.A,1);
           inc(acounter);
           if acounter = Width then
            begin
             AlphaStream.Seek(aGap,soCurrent);
             acounter := 0;
            end;
          end;
         OutputStream.Write(ARGB,4);
        end;
      end;
 end;
end;

function GrayscalePalette : TPalette;
var i : integer; Palette : TPalette; ARGB : TARGB;
begin
 for i := 0 to 255 do
  begin
   with ARGB do
    begin
     B := i;
     G := i;
     R := i;
     A := 0;
    end;
   Palette.Palette[i] := ARGB;
  end;
 GrayscalePalette := Palette;
end;

end.

Код вышел лапшевидный, зато полностью работоспособный. :)

З.Ы. HoRRoR, извини, что по-пусту доставал тебя с этой процедурой по аське. ^_^

0

29

O'кей, думаю что какие-то пояснения все же нужно дать. Итак, как видно из вышеприведенного листинга, после расшифровки и распаковки файла mask.cps происходит следующее:

1. Вызывается функция, которую я обозвал PRTStructInit
2. Копируется какое-то количество байт из одной области памяти в другую.

Копируется ни что иное, как содержимое 'растрового массива', смещение на начало которого записано в поле Offset2 PRT-заголовка в буфер, созданный ОС для pixel data of device independent bitmap, т.е. копируются сами данные изображения. Длина их высчитывается по формуле

- ( pPRTStructTbl[ Index ] -> ScanlineLen ) * Height

Кстати, '->' это оператор доступа к члену структуры через указатель на эту структуру. Указатель в данном случае берется из массива указателей pPRTStructTbl (элемент с индексом Index).

pPRTStructTbl[ Index ] -> ScanlineLen же высчитывается в теле функции PRTStructInit в switch-case, затем

Код:
    if( aHeight > 0 )
        pPRTStructTbl[ aIndex ] -> ScanlineLen = - ( pPRTStructTbl[ aIndex ] -> ScanlineLen );

как видно из этого кода, в случае если aHeight (PRTHeader.Height) больше 0, знак ScanlineLen меняется на противоположный (в чем смысл этого финта ушами мне пока непонятно).
Картинка если и рендерится, рендерится где-то в другом месте, поэтому о предназначении Width3 и Height3 сказать сложно. Могу сказать одно: после вызова функции PRTStructInit Width3, Width2 и Width1 будут содержать одно и то же значение – PRTHeader.Width. Аналогично и с Height3.

И еще в приведенном коде нет и намека на альфу. Даже массив из структур RGBQUAD (цветовые данные изображения), как видишь, перед вызовом CreateDIBSection ничем не заполняется...

Отредактировано Serke (2007-12-08 14:17:47)

0

30

Небольшое дополнение: присловутая альфа в PRT хранится, ВНИМАНИЕ, без выравнивания и в ПЕРЕВЁРНУТОМ ПО ВЕРТИКАЛИ состоянии.

Т.е. исполнение кода-антивыравнивателя ...

Код:
inc(acounter);
if acounter = Width then
 begin
  AlphaStream.Seek(aGap,soCurrent);
  acounter := 0;
 end;

... для неё НЕ требуется, зато требуется читать сканлинии в обратном порядке - с последней по первую. :)

Добавил в функцию переменную InterleavedAlpha. ^_^ Продолжение следует...

EDIT: Вот промежуточный результат конверсии. Получена путём чтения через модуль GrapS со смещением в 1 байт (сделал специально, чтобы было видно альфу).

Отредактировано WinKi (2007-12-08 15:09:25)

0


Вы здесь » Старые Консольные Игры » Разрабатываемые проекты » Ever17 - the out of infinity (PC)