unit kolPortalDatConvert; interface uses Windows, KOL, mmsystem, kolDXTDecompress, kolPortalDatConvert_const_types; const // Image types it_24bit = $00000014; // 20 it_32bit = $00000015; // 21 it_JPG = $000001F4; // 500 it_DXT1 = $31545844; // DXT1 it_DXT2 = $32545844; // DXT2 it_DXT3 = $33545844; // DXT3 type // BMP . AC2ImageHeader = packed record FileID : LongWord; UnkTag : LongWord; Width : LongWord; Height : LongWord; Type_ : LongWord; Size : LongWord; end; // WAV . PMP3WaveFormat = ^TMP3WaveFormat; TMP3WaveFormat = packed record wfx : TWaveFormatEx; wID : WORD; fdwFlags : DWORD; nBlockSize : WORD; nFramesPerBlock : WORD; nCodecDelay : WORD; end; PACSound = ^ACSound; ACSound = packed record ID : DWORD; Fmt : DWORD; Size : DWORD; Case DWORD of $1 : (wavx : TWaveFormatEx); $2 : (mp3 : TMP3WaveFormat); $3 : (data : Array[0..0] of Char); end; // DXT . ACDXTHeader = packed record ID : DWORD; Flags : DWORD; Width : DWORD; Height : DWORD; Format : DWORD; Size : DWORD; end; // BMP functions function AC2BmpToBmp(const InStream: PStream; OutBitmap: PBitmap): Boolean; function BmpToAC2Bmp(const InBitmap: PBitmap; OutStream: PStream): Boolean; procedure AC2BmpSetData(const DataStream: PStream; FileID, UnkTag: LongWord); // DXT functions function AC2DXTToBMP(const InStream: PStream; OutBitmap: PBitmap): Boolean; // -- function AC2DXTToDDS(const InStream, OutStream: PStream): Boolean; function DDSToAC2DXT1(const InStream, OutStream: PStream): Boolean; function DDSToAC2DXT2(const InStream, OutStream: PStream): Boolean; function DDSToBmp(const InStream: PStream; OutBitmap: PBitmap): Boolean; // JPG functions function AC2JpgToJpg(const InStream, OutJpgStream: PStream): Boolean; function JpgToAC2Jpg(const InJpgStream, OutStream: PStream): Boolean; procedure AC2JpgSetData(const DataStream: PStream; FileID, UnkTag, Size: LongWord); // WAV functions function AC2WavToWav(const InMemStream, OutMemWave: PStream): Boolean; function WavToAC2Wav(const InMemWave, OutMemStream: PStream): Boolean; procedure AC2WavSetData(const DataStream: PStream; FileID: LongWord); // Memory Stream functions procedure FileToMemStream(const AFilename: String; const AMemStrm: PStream); procedure MemStreamToFile(const AMemStrm: PStream; const AFilename: String); implementation // ========================================== // BMP functions // ========================================== function AC2BmpToBmp(const InStream: PStream; OutBitmap: PBitmap): Boolean; var iWidth, iHeight, iType, iSize : LongWord; y : Integer; Row3 : pRGBTripleArray; Row4 : pRGBQuadArray; begin result := False; if OutBitmap = nil then Exit; InStream.Seek( 8, spBegin ); InStream.Read( iWidth, 4 ); InStream.Read( iHeight, 4 ); InStream.Read( iType, 4 ); InStream.Read( iSize, 4 ); // Insanity check. if (iWidth > 10000) or (iHeight > 10000) then Exit; if (iType in [$14..$15]) then begin OutBitmap.Width := iWidth; OutBitmap.Height := iHeight; // 32bit bitmap. if iType = $15 then begin OutBitmap.PixelFormat := pf32bit; for y := 0 to (OutBitmap.Height-1) do begin Row4 := OutBitmap.Scanline[y]; InStream.Read(Row4^,OutBitmap.Width*4); end; end else // 24bit bitmap if iType = $14 then begin OutBitmap.PixelFormat := pf24bit; for y := 0 to (OutBitmap.Height-1) do begin Row3 := OutBitmap.Scanline[y]; InStream.Read(Row3^,OutBitmap.Width*3); end; end; end; result := True; end; function BmpToAC2Bmp(const InBitmap: PBitmap; OutStream: PStream): Boolean; var iUnk, iWidth, iHeight, iType, iSize : LongWord; y : Integer; Row3 : pRGBTripleArray; Row4 : pRGBQuadArray; begin result := False; iUnk := 0; iWidth := InBitmap.Width; iHeight := InBitmap.Height; case InBitmap.PixelFormat of pf32bit : begin iType := $15; iSize := iWidth * iHeight * 4; end; pf24bit : begin iType := $14; iSize := iWidth * iHeight * 3; end; else Exit; end; OutStream.Write(iUnk,4); // No FileID OutStream.Write(iUnk,4); // No Unk either OutStream.Write(iWidth,4); OutStream.Write(iHeight,4); OutStream.Write(iType,4); OutStream.Write(iSize,4); // 32bit bitmap. if iType = $15 then begin for y := 0 to (InBitmap.Height-1) do begin Row4 := InBitmap.Scanline[y]; OutStream.Write(Row4^,InBitmap.Width*4); end; end else // 24bit bitmap if iType = $14 then begin for y := 0 to (InBitmap.Height-1) do begin Row3 := InBitmap.Scanline[y]; OutStream.Write(Row3^,InBitmap.Width*3); end; end; result := True; end; procedure AC2BmpSetData(const DataStream: PStream; FileID, UnkTag: LongWord); var iBuf : LongWord; begin DataStream.Seek(0,spBegin); iBuf := FileID; DataStream.Write(iBuf,4); iBuf := UnkTag; DataStream.Write(iBuf,4); end; // ========================================== // DXT functions // ========================================== function AC2DXTToBMP(const InStream: PStream; OutBitmap: PBitmap): Boolean; var tmp_strm : PStream; begin result := False; tmp_strm := NewMemoryStream; try AC2DXTToDDS(InStream,tmp_strm); tmp_strm.Position := 0; MemStreamToFile(tmp_strm,'x:\testing.dds'); tmp_strm.Position := 0; DDSToBmp(tmp_strm,OutBitmap); finally tmp_strm.Free; end; end; function AC2DXTToDDS(const InStream, OutStream: PStream): Boolean; var ac2hdr : ACDXTHeader; dds_tag : LongWord; ddsd_ : DDSURFACEDESC2; begin result := False; if InStream = nil then Exit; if OutStream = nil then Exit; // 1) Read AC2 header. InStream.Position := 0; OutStream.Position := 0; InStream.Read(ac2hdr,SizeOf(ac2hdr)); // 2) Convert to a dds FillChar(ddsd_,SizeOf(ddsd_),#0); dds_tag := FOURCC_DDS; OutStream.Write(dds_tag,SizeOf(dds_tag)); ddsd_.dwSize := SizeOf(ddsd_); // ddsd_.dwFlags := ac2hdr.Flags; ddsd_.dwWidth := ac2hdr.Width; ddsd_.dwHeight := ac2hdr.Height; ddsd_.ddpfPixelFormat.dwFourCC := ac2hdr.Format; OutStream.Write(ddsd_,SizeOf(ddsd_)); Stream2StreamEx(OutStream,InStream,ac2hdr.Size); end; function DDSToAC2DXT1(const InStream, OutStream: PStream): Boolean; begin result := False; if InStream = nil then Exit; if OutStream = nil then Exit; end; function DDSToAC2DXT2(const InStream, OutStream: PStream): Boolean; begin result := False; if InStream = nil then Exit; if OutStream = nil then Exit; end; function DDSToBmp(const InStream: PStream; OutBitmap: PBitmap): Boolean; var DDSImg : PDXTCImage; begin result := False; if InStream = nil then Exit; if OutBitmap = nil then Exit; DDSImg := NewDXTCImage; try result := DDSImg.LoadFromStream(InStream); if result then begin DDSImg.Decompress; result := DDSImg.SaveToBmp(OutBitmap); end; finally DDSImg.Free; end; end; // ========================================== // JPG functions // ========================================== function AC2JpgToJpg(const InStream, OutJpgStream: PStream): Boolean; var FileHeader : AC2ImageHeader; JpgBuffer : Pointer; begin result := False; InStream.Position := 0; InStream.Read(FileHeader,SizeOf(FileHeader)); // Sanity check. if FileHeader.Size > 2097152 then Exit; // 2mb large jpg? I doubt it :-) if FileHeader.Type_ = it_JPG then begin GetMem(JpgBuffer,FileHeader.Size); try InStream.Read(JpgBuffer^,FileHeader.Size); OutJpgStream.Write(JpgBuffer^,FileHeader.Size); finally FreeMem(JpgBuffer,FileHeader.Size); end; result := True; end; end; function JpgToAC2Jpg(const InJpgStream, OutStream: PStream): Boolean; var FileHeader : AC2ImageHeader; JpgBuffer : Pointer; begin InJpgStream.Position := 0; FileHeader.FileID := 0; FileHeader.UnkTag := 0; FileHeader.Width := 0; FileHeader.Height := 0; FileHeader.Type_ := it_JPG; FileHeader.Size := InJpgStream.Size; OutStream.Position := 0; OutStream.Write(FileHeader,SizeOf(FileHeader)); GetMem(JpgBuffer,InJpgStream.Size); try InJpgStream.Read(JpgBuffer^,InJpgStream.Size); OutStream.Write(JpgBuffer^,InJpgStream.Size); result := True; finally FreeMem(JpgBuffer,InJpgStream.Size); end; end; procedure AC2JpgSetData(const DataStream: PStream; FileID, UnkTag, Size: LongWord); var iBuf : LongWord; begin DataStream.Seek(0,spBegin); iBuf := FileID; DataStream.Write(iBuf,4); iBuf := UnkTag; DataStream.Write(iBuf,4); end; // ========================================== // WAV functions // ========================================== function AC2WavToWav(const InMemStream, OutMemWave: PStream): Boolean; var _wavIOHandle : Integer; _wavIOData : PACSound; _wavIORealData : Pointer; _wavIORIFF : TMMCKINFO; _wavIOck : TMMCKINFO; _wavFileInfo : TMMIOInfo; begin result := False; InMemStream.Seek(0,spBegin); _wavIOData := InMemStream.Memory; OutMemWave.Size := InMemStream.Size + SizeOf(TMMCKINFO) - 4; FillChar(_wavFileInfo, sizeof(TMMIOINFO), 0); _wavFileInfo.dwFlags := MMIO_CREATE OR MMIO_WRITE OR MMIO_COMPAT OR MMIO_ALLOCBUF; _wavFileInfo.fccIOProc := FOURCC_MEM; _wavFileInfo.pchBuffer := OutMemWave.Memory; _wavFileInfo.cchBuffer := OutMemWave.Size; _wavIOHandle := mmioOpen(nil,@_wavFileInfo,MMIO_CREATE OR MMIO_WRITE OR MMIO_COMPAT OR MMIO_ALLOCBUF); if _wavIOHandle <> 0 then begin if _wavIOData.wavx.nSamplesPerSec = 22050 then _wavIOData.wavx.nBlockAlign := 4; _wavIOData.wavx.cbSize := SizeOf(_wavIOData.wavx); // // create the RIFF chunk of form type 'WAVE' // FillChar(_wavIORIFF,SizeOf(TMMCKINFO),#0); _wavIORIFF.fccType := mmioStringToFOURCC('WAVE',0); _wavIORIFF.ckID := mmioStringToFOURCC('RIFF',0); mmioCreateChunk(_wavIOHandle, @_wavIORIFF, MMIO_CREATERIFF); // create the format chunk and write the format header into it FillChar(_wavIOck,SizeOf(TMMCKINFO),#0); _wavIOck.ckID := mmioStringToFOURCC('fmt ',0); mmioCreateChunk(_wavIOHandle, @_wavIOck, 0); mmioWrite(_wavIOHandle, PChar(@(_wavIOData^.wavx)), SizeOf(TWaveFormatEx)); mmioAscend(_wavIOHandle, @_wavIOck, 0); // create the data chunk with the file data _wavIOck.ckID := mmioStringToFOURCC('data',0); mmioCreateChunk(_wavIOHandle, @_wavIOck, 0); _wavIORealData := Pointer(Integer(@_wavIOData.data)+SizeOf(TWaveFormatEx)); // mmioWrite(_wavIOHandle,_wavIORealData,_wavIOData.Size-SizeOf(TWaveFormatEx)); mmioWrite(_wavIOHandle,_wavIORealData,_wavIOData.Size); mmioAscend(_wavIOHandle, @_wavIOck, 0); // close off the RIFF chunk mmioAscend(_wavIOHandle, @_wavIORIFF, 0); mmioClose(_wavIOHandle, 0); result := True; end; end; function WavToAC2Wav(const InMemWave, OutMemStream: PStream): Boolean; const fcc_fmt : LongWord = $20746D66; fcc_data : LongWord = $61746164; var iEmpty : LongWord; iHdrSize : LongWord; iDataSize : LongWord; pHeader : Pointer; pData : Pointer; function SeekToFourCC(const FourCC: LongWord): Boolean; var ccBuf : LongWord; cnt : Integer; begin result := False; for cnt := 1 to 400 do // max 400 bytes begin InMemWave.Position := cnt; InMemWave.Read(ccBuf,4); if ccBuf = FourCC then begin result := True; Break; end; end; end; begin result := False; (* AC2 Wave header is: FileID : LongWord; HdrSize : LongWord; DataSize : LongWord; Header : ABuffer; Data : ABuffer; *) // Seek to 'fmt ' if SeekToFourCC(fcc_fmt) = False then Exit; InMemWave.Read(iHdrSize,4); if iHdrSize > 100 then Exit; GetMem(pHeader,iHdrSize); try InMemWave.Read(pHeader^,iHdrSize); // Seek to 'data' if SeekToFourCC(fcc_data) = False then Exit; InMemWave.Read(iDataSize,4); GetMem(pData,iDataSize); try InMemWave.Read(pData^,iDataSize); // Write an empty FileID OutMemStream.Position := 0; iEmpty := 0; OutMemStream.Write(iEmpty,4); OutMemStream.Write(iHdrSize,4); OutMemStream.Write(iDataSize,4); OutMemStream.Write(pHeader^,iHdrSize); OutMemStream.Write(pData^,iDataSize); result := True; finally FreeMem(pData,iDataSize); end; finally FreeMem(pHeader,iHdrSize); end; end; procedure AC2WavSetData(const DataStream: PStream; FileID: LongWord); var iBuf : LongWord; begin DataStream.Seek(0,spBegin); iBuf := FileID; DataStream.Write(iBuf,4); end; procedure FileToMemStream(const AFilename: String; const AMemStrm: PStream); var InFileStream: PStream; begin InFileStream := NewReadFileStream(AFilename); try AMemStrm.Position := 0; Stream2Stream(AMemStrm,InFileStream,InFileStream.Size); finally InFileStream.Free; end; end; procedure MemStreamToFile(const AMemStrm: PStream; const AFilename: String); var OutFileStream: PStream; begin OutFileStream := NewWriteFileStream(AFilename); try AMemStrm.Position := 0; Stream2Stream(OutFileStream,AMemStrm,AMemStrm.Size); finally OutFileStream.Free; end; end; end.