From: DeaDDooMER Date: Mon, 4 Sep 2023 12:00:04 +0000 (+0300) Subject: Revert to old wad read/write method X-Git-Url: https://deadsoftware.ru/gitweb?p=d2df-editor.git;a=commitdiff_plain;h=4cd27745126226e5e57a02f4c2a44ccbab16bbac Revert to old wad read/write method --- diff --git a/lang/editor.ru_RU.lng b/lang/editor.ru_RU.lng index 681f634..29bf4c9 100644 --- a/lang/editor.ru_RU.lng +++ b/lang/editor.ru_RU.lng @@ -1222,10 +1222,10 @@ g_language.MsgWadSpecialMap = "" g_language.MsgWadSpecialTexs$ = "" g_language.MsgWadSpecialTexs = "<СПЕЦТЕКСТУРЫ>" -g_language.MsgFileFilterAll$ = "Doom 2D: Forever Maps (*.dfz, *.dfzip, *.zip, *.wad)|*.dfz;*.dfzip;*.zip;*.wad|Doom 2D: Forever 0.30 Maps (*.ini)|*.ini|All Files (*.*)|*.*" -g_language.MsgFileFilterAll = "Карты Doom 2D: Forever (*.dfz, *.dfzip, *.zip, *.wad)|*.dfz;*.dfzip;*.zip;*.wad|Старые карты Doom 2D: Forever 0.30 (*.ini)|*.ini|Все файлы (*.*)|*.*" -g_language.MsgFileFilterWad$ = "Doom 2D: Forever Maps (*.dfz)|*.dfz|Doom 2D: Forever Maps (*.dfzip)|*.dfzip|Doom 2D: Forever Maps (*.zip)|*.zip|Doom 2D: Forever Maps (*.wad)|*.wad|All Files (*.*)|*.*" -g_language.MsgFileFilterWad = "Карты Doom 2D: Forever (*.dfz)|*.dfz|Карты Doom 2D: Forever (*.dfzip)|*.dfzip|Карты Doom 2D: Forever (*.zip)|*.zip|Карты Doom 2D: Forever (*.wad)|*.wad|Все файлы (*.*)|*.*" +g_language.MsgFileFilterAll$ = "Doom 2D: Forever Maps (*.wad)|*.wad|Doom 2D: Forever 0.30 Maps (*.ini)|*.ini|All Files (*.*)|*.*" +g_language.MsgFileFilterAll = "Карты Doom 2D: Forever (*.wad)|*.wad|Старые карты Doom 2D: Forever 0.30 (*.ini)|*.ini|Все файлы (*.*)|*.*" +g_language.MsgFileFilterWad$ = "Doom 2D: Forever Maps (*.wad)|*.wad|All Files (*.*)|*.*" +g_language.MsgFileFilterWad = "Карты Doom 2D: Forever (*.wad)|*.wad|Все файлы (*.*)|*.*" g_language.MsgFileFilterExeMac$ = "Doom 2D Forever.app|*.app|Doom 2D Forever (Unix Executable)|Doom2DF;*" g_language.MsgFileFilterExeMac = "Doom 2D Forever.app|*.app|Doom 2D Forever (Исполняемый файл)|Doom2DF;*" g_language.MsgFileFilterExeWin$ = "Doom2DF.exe|Doom2DF.exe;*.exe" diff --git a/src/editor/Editor.lpi b/src/editor/Editor.lpi index 1354cd3..c958b71 100644 --- a/src/editor/Editor.lpi +++ b/src/editor/Editor.lpi @@ -44,13 +44,12 @@ - - + diff --git a/src/editor/Editor.lpr b/src/editor/Editor.lpr index 6bf0b86..eaf830d 100644 --- a/src/editor/Editor.lpr +++ b/src/editor/Editor.lpr @@ -18,12 +18,6 @@ uses WADEDITOR in '../shared/WADEDITOR.pas', WADSTRUCT in '../shared/WADSTRUCT.pas', CONFIG in '../shared/CONFIG.pas', - xstreams in '../shared/xstreams.pas', - dfzip in '../shared/dfzip.pas', - sfs in '../sfs/sfs.pas', - sfsPlainFS in '../sfs/sfsPlainFS.pas', - sfsZipFS in '../sfs/sfsZipFS.pas', - f_about in 'f_about.pas' {AboutForm}, f_options in 'f_options.pas' {OptionsForm}, f_main in 'f_main.pas' {MainForm}, diff --git a/src/editor/f_addresource.pas b/src/editor/f_addresource.pas index 103d9b0..a0bebb9 100644 --- a/src/editor/f_addresource.pas +++ b/src/editor/f_addresource.pas @@ -45,7 +45,7 @@ var implementation uses - f_main, WADSTRUCT, g_language, utils, sfs, g_options; + f_main, WADSTRUCT, g_language, utils, g_options; {$R *.lfm} @@ -53,8 +53,9 @@ const STANDART_WAD = 'standart.wad'; procedure TAddResourceForm.FormActivate(Sender: TObject); - var - SR: TSearchRec; +var + SR: TSearchRec; + begin cbWADList.Clear(); cbSectionsList.Clear(); @@ -66,8 +67,7 @@ begin if FindFirst(WadsDir + DirectorySeparator + '*.*', faAnyFile, SR) = 0 then repeat - if (SR.name <> '.') and (SR.name <> '..') then - cbWADList.Items.Add(SR.Name); + cbWADList.Items.Add(SR.Name); until FindNext(SR) <> 0; FindClose(SR); @@ -102,67 +102,87 @@ begin end; procedure TAddResourceForm.cbWADListChange(Sender: TObject); - var - wad: TSFSFileList; - i: Integer; - FileName, Section, sn, rn: String; +var + WAD: TWADEditor_1; + SectionList: SArray; + i: Integer; + FileName, fn, sn, rn: String; + begin + WAD := TWADEditor_1.Create(); + +// Внешний WAD: if cbWADList.Text <> MsgWadSpecialMap then - FileName := WadsDir + DirectorySeparator + cbWADList.Text (* Resource wad *) - else - g_ProcessResourceStr(OpenedMap, FileName, sn, rn); (* Map wad *) + FileName := WadsDir + DirectorySeparator + cbWADList.Text + else // WAD карты: + begin + g_ProcessResourceStr(OpenedMap, fn, sn, rn); + FileName := fn; + end; + +// Читаем секции: + WAD.ReadFile(FileName); + SectionList := WAD.GetSectionList(); + WAD.Free(); cbSectionsList.Clear(); lbResourcesList.Clear(); - wad := SFSFileList(FileName); - if wad <> nil then - begin - for i := 0 to wad.Count - 1 do - begin - Section := win2utf(Copy(wad.Files[i].path, 1, Length(wad.Files[i].path) - 1)); - if cbSectionsList.Items.IndexOf(Section) = -1 then - cbSectionsList.Items.Add(Section) - end; - wad.Destroy - end; - - (* Update resource list (see below) *) - cbSectionsListChange(Sender) + if SectionList <> nil then + for i := 0 to High(SectionList) do + if SectionList[i] <> '' then + cbSectionsList.Items.Add(win2utf(SectionList[i])) + else + cbSectionsList.Items.Add('..'); end; procedure TAddResourceForm.cbSectionsListChange(Sender: TObject); - var - wad: TSFSFileList; - i: Integer; - FileName, Section, SectionName, sn, rn: String; +var + ResourceList: SArray; + WAD: TWADEditor_1; + i: DWORD; + FileName, SectionName, fn, sn, rn: String; + begin + WAD := TWADEditor_1.Create(); + +// Внешний WAD: if cbWADList.Text <> MsgWadSpecialMap then - FileName := WadsDir + DirectorySeparator + cbWADList.Text (* Resource wad *) + FileName := WadsDir + DirectorySeparator + cbWADList.Text + else // WAD карты: + begin + g_ProcessResourceStr(OpenedMap, fn, sn, rn); + FileName := fn; + end; + +// Читаем WAD: + WAD.ReadFile(FileName); + + if cbSectionsList.Text <> '..' then + SectionName := cbSectionsList.Text else - g_ProcessResourceStr(OpenedMap, FileName, sn, rn); (* Map wad *) + SectionName := ''; + +// Читаем ресурсы выбранной секции: + ResourceList := WAD.GetResourcesList(utf2win(SectionName)); + + WAD.Free(); - SectionName := cbSectionsList.Text; lbResourcesList.Clear(); - wad := SFSFileList(FileName); - if wad <> nil then - begin - for i := 0 to wad.Count - 1 do - begin - Section := win2utf(Copy(wad.Files[i].path, 1, Length(wad.Files[i].path) - 1)); - if Section = SectionName then - lbResourcesList.Items.Add(win2utf(wad.Files[i].name)) - end; - wad.Destroy - end; + if ResourceList <> nil then + for i := 0 to High(ResourceList) do + lbResourcesList.Items.Add(win2utf(ResourceList[i])); end; procedure TAddResourceForm.lbResourcesListClick(Sender: TObject); - var - FileName, fn: String; +var + FileName, SectionName, fn: String; + begin - FResourceSelected := (lbResourcesList.SelCount > 0) or (lbResourcesList.ItemIndex > -1); + FResourceSelected := (lbResourcesList.SelCount > 0) or + (lbResourcesList.ItemIndex > -1); + if not FResourceSelected then begin FResourceName := ''; @@ -170,18 +190,25 @@ begin Exit; end; + if cbSectionsList.Text = '..' then + SectionName := '' + else + SectionName := cbSectionsList.Text; + if cbWADList.Text[1] <> '<' then FileName := cbWADList.Text else FileName := ''; - FResourceName := FileName + ':' + cbSectionsList.Text + '\' + lbResourcesList.Items[lbResourcesList.ItemIndex]; + FResourceName := FileName+':'+SectionName+'\'+lbResourcesList.Items[lbResourcesList.ItemIndex]; - g_ProcessResourceStr(OpenedMap, @fn, nil, nil); if FileName <> '' then FFullResourceName := WadsDir + DirectorySeparator + FResourceName else - FFullResourceName := fn + FResourceName + begin + g_ProcessResourceStr(OpenedMap, @fn, nil, nil); + FFullResourceName := fn+FResourceName; + end; end; end. diff --git a/src/editor/f_addresource_sky.pas b/src/editor/f_addresource_sky.pas index bb4fa10..e7b0361 100644 --- a/src/editor/f_addresource_sky.pas +++ b/src/editor/f_addresource_sky.pas @@ -31,7 +31,7 @@ var implementation uses - WADEDITOR, f_main, g_language, g_resources; + BinEditor, WADEDITOR, f_main, g_language; {$R *.lfm} @@ -47,14 +47,23 @@ var TextureData: Pointer; ImageSize: Integer; + WAD: TWADEditor_1; WADName: String; SectionName: String; ResourceName: String; begin Result := nil; + +// Загружаем ресурс текстуры из WAD: g_ProcessResourceStr(ResourceStr, WADName, SectionName, ResourceName); - g_ReadResource(WADName, SectionName, ResourceName, TextureData, ImageSize); + + WAD := TWADEditor_1.Create(); + WAD.ReadFile(WADName); + + WAD.GetResource(utf2win(SectionName), utf2win(ResourceName), TextureData, ImageSize); + + WAD.Free(); (* !!! copypaste from f_addresource_texture.CreateBitMap *) diff --git a/src/editor/f_addresource_sound.pas b/src/editor/f_addresource_sound.pas index a3d3fe1..eaba574 100644 --- a/src/editor/f_addresource_sound.pas +++ b/src/editor/f_addresource_sound.pas @@ -45,7 +45,7 @@ var implementation uses - BinEditor, WADEDITOR, e_log, f_main, g_language, g_resources + BinEditor, WADEDITOR, e_log, f_main, g_language {$IFNDEF NOSOUND}, fmod, fmodtypes, fmoderrors;{$ELSE};{$ENDIF} {$R *.lfm} @@ -121,6 +121,7 @@ end; function TAddSoundForm.CreateSoundWAD(Resource: String): Boolean; var + WAD: TWADEditor_1; FileName, SectionName, ResourceName: String; ResLength: Integer; sz: LongWord; @@ -138,9 +139,11 @@ begin {$IFNDEF NOSOUND} g_ProcessResourceStr(Resource, FileName, SectionName, ResourceName); - g_ReadResource(FileName, SectionName, ResourceName, SoundData, ResLength); - if SoundData <> nil then + WAD := TWADEditor_1.Create; + WAD.ReadFile(FileName); + + if WAD.GetResource(utf2win(SectionName), utf2win(ResourceName), SoundData, ResLength) then begin sz := SizeOf(FMOD_CREATESOUNDEXINFO); FillMemory(@soundExInfo, sz, 0); @@ -155,16 +158,19 @@ begin begin e_WriteLog(Format('Error creating sound %s', [Resource]), MSG_WARNING); e_WriteLog(FMOD_ErrorString(res), MSG_WARNING); + WAD.Free(); Exit; end; end else begin e_WriteLog(Format('Error loading sound %s', [Resource]), MSG_WARNING); - //e_WriteLog(Format('WAD Reader error: %s', [WAD.GetLastErrorStr]), MSG_WARNING); + e_WriteLog(Format('WAD Reader error: %s', [WAD.GetLastErrorStr]), MSG_WARNING); + WAD.Free(); Exit; end; + WAD.Free(); Result := True; {$ENDIF} end; diff --git a/src/editor/f_addresource_texture.pas b/src/editor/f_addresource_texture.pas index 927017e..13adf3c 100644 --- a/src/editor/f_addresource_texture.pas +++ b/src/editor/f_addresource_texture.pas @@ -49,48 +49,196 @@ implementation uses BinEditor, WADEDITOR, WADSTRUCT, f_main, g_textures, CONFIG, g_map, - g_language, e_Log, g_resources; + g_language; {$R *.lfm} function IsAnim(Res: String): Boolean; - var - data: Pointer; - len: Integer; - WADName, SectionName, ResourceName: String; +var + WAD: TWADEditor_1; + WADName: String; + SectionName: String; + ResourceName: String; + Data: Pointer; + Size: Integer; + Sign: Array [0..4] of Char; + Sections, + Resources: SArray; + a: Integer; + ok: Boolean; + begin + Result := False; + Data := nil; + Size := 0; + +// Читаем файл и ресурс в нем: g_ProcessResourceStr(Res, WADName, SectionName, ResourceName); - (* just check file existance *) - g_ReadSubResource(WADName, SectionName, ResourceName, 'TEXT', 'ANIM', data, len); - (* TODO check section TEXTURES *) - Result := data <> nil; - if data <> nil then - FreeMem(data) + + WAD := TWADEditor_1.Create(); + + if (not WAD.ReadFile(WADName)) or + (not WAD.GetResource(utf2win(SectionName), utf2win(ResourceName), Data, Size)) then + begin + WAD.Free(); + Exit; + end; + + WAD.FreeWAD(); + +// Проверка сигнатуры. Если есть - это WAD внутри WAD: + CopyMemory(@Sign[0], Data, 5); + + if not (Sign = DFWAD_SIGNATURE) then + begin + WAD.Free(); + FreeMem(Data); + Exit; + end; + +// Пробуем прочитать данные: + if not WAD.ReadMemory(Data, Size) then + begin + WAD.Free(); + FreeMem(Data); + Exit; + end; + + FreeMem(Data); + +// Читаем секции: + Sections := WAD.GetSectionList(); + + if Sections = nil then + begin + WAD.Free(); + Exit; + end; + +// Ищем в секциях "TEXT": + ok := False; + for a := 0 to High(Sections) do + if Sections[a] = 'TEXT' then + begin + ok := True; + Break; + end; + +// Ищем в секциях лист текстур - "TEXTURES": + for a := 0 to High(Sections) do + if Sections[a] = 'TEXTURES' then + begin + ok := ok and True; + Break; + end; + + if not ok then + begin + WAD.Free(); + Exit; + end; + +// Получаем ресурсы секции "TEXT": + Resources := WAD.GetResourcesList('TEXT'); + + if Resources = nil then + begin + WAD.Free(); + Exit; + end; + +// Ищем в них описание анимации - "ANIM": + ok := False; + for a := 0 to High(Resources) do + if Resources[a] = 'ANIM' then + begin + ok := True; + Break; + end; + + WAD.Free(); + +// Если все получилось, то это аним. текстура: + Result := ok; end; -function GetFrame (Res: String; var Data: Pointer; var DataLen: Integer; var Width, Height: Word): Boolean; - var - Len: Integer; - TextData: Pointer; - WADName, SectionName, ResourceName: String; - config: TConfig; +function GetFrame(Res: String; var Data: Pointer; var DataLen: Integer; var Width, Height: Word): Boolean; +var + AnimWAD: Pointer; + WAD: TWADEditor_1; + WADName: String; + SectionName: String; + ResourceName: String; + Len: Integer; + config: TConfig; + TextData: Pointer; + begin - Result := False; Data := nil; DataLen := 0; Width := 0; Height := 0; + Result := False; + AnimWAD := nil; + Len := 0; + TextData := nil; + +// Читаем WAD: g_ProcessResourceStr(Res, WADName, SectionName, ResourceName); - g_ReadSubResource(WADName, SectionName, ResourceName, 'TEXT', 'ANIM', TextData, Len); - if TextData <> nil then + + WAD := TWADEditor_1.Create(); + + if not WAD.ReadFile(WADName) then begin - config := TConfig.CreateMem(TextData, Len); - g_ReadSubResource(WADName, SectionName, ResourceName, 'TEXTURES', config.ReadStr('', 'resource', ''), Data, DataLen); - if Data <> nil then - begin - Height := config.ReadInt('', 'frameheight', 0); - Width := config.ReadInt('', 'framewidth', 0); - Result := True - end; - config.Free(); - FreeMem(TextData) - end + WAD.Free(); + Exit; + end; + +// Читаем WAD-ресурс из WAD: + if not WAD.GetResource(utf2win(SectionName), utf2win(ResourceName), AnimWAD, Len) then + begin + WAD.Free(); + Exit; + end; + + WAD.FreeWAD(); + +// Читаем WAD в WAD'е: + if not WAD.ReadMemory(AnimWAD, Len) then + begin + FreeMem(AnimWAD); + WAD.Free(); + Exit; + end; + +// Читаем описание анимации: + if not WAD.GetResource('TEXT', 'ANIM', TextData, Len) then + begin + FreeMem(TextData); + FreeMem(AnimWAD); + WAD.Free(); + Exit; + end; + + config := TConfig.CreateMem(TextData, Len); + +// Читаем ресурс - лист текстур: + if not WAD.GetResource('TEXTURES', config.ReadStr('', 'resource', ''), Data, Len) then + begin + FreeMem(TextData); + FreeMem(AnimWAD); + WAD.Free(); + Exit; + end; + + DataLen := Len; + + Height := config.ReadInt('', 'frameheight', 0); + Width := config.ReadInt('', 'framewidth', 0); + + config.Free(); + WAD.Free(); + + FreeMem(TextData); + FreeMem(AnimWAD); + + Result := True; end; function CreateBitMap (Data: Pointer; DataSize: Cardinal): TBitMap; @@ -144,44 +292,94 @@ begin end; function ShowAnim(Res: String): TBitMap; - var - Len: Integer; - TextData, TextureData: Pointer; - WADName, SectionName, ResourceName: String; - config: TConfig; +var + AnimWAD: Pointer; + WAD: TWADEditor_1; + WADName: String; + SectionName: String; + ResourceName: String; + Len: Integer; + config: TConfig; + TextData: Pointer; + TextureData: Pointer; + begin Result := nil; + AnimWAD := nil; + Len := 0; + TextData := nil; + TextureData := nil; + +// Читаем WAD файл и ресурс в нем: g_ProcessResourceStr(Res, WADName, SectionName, ResourceName); - g_ReadSubResource(WADName, SectionName, ResourceName, 'TEXT', 'ANIM', TextData, Len); - if TextData <> nil then + + WAD := TWADEditor_1.Create(); + WAD.ReadFile(WADName); + WAD.GetResource(utf2win(SectionName), utf2win(ResourceName), AnimWAD, Len); + WAD.FreeWAD(); + +// Читаем описание анимации: + WAD.ReadMemory(AnimWAD, Len); + WAD.GetResource('TEXT', 'ANIM', TextData, Len); + + config := TConfig.CreateMem(TextData, Len); + +// Читаем лист текстур: + WAD.GetResource('TEXTURES', config.ReadStr('', 'resource', ''), TextureData, Len); + NumFrames := config.ReadInt('', 'framecount', 0); + + if (TextureData <> nil) and + (WAD.GetLastError = DFWAD_NOERROR) then begin - config := TConfig.CreateMem(TextData, Len); - g_ReadSubResource(WADName, SectionName, ResourceName, 'TEXTURES', config.ReadStr('', 'resource', ''), TextureData, Len); - if TextureData <> nil then - begin - Result := CreateBitMap(TextureData, Len); - (* view only first frame *) - NumFrames := config.ReadInt('', 'framecount', 0); - Result.Height := config.ReadInt('', 'frameheight', 0); - Result.Width := config.ReadInt('', 'framewidth', 0); - FreeMem(TextureData) - end; - config.Free(); - FreeMem(TextData) - end + // Создаем BitMap из листа текстур: + Result := CreateBitMap(TextureData, Len); + + // Размеры одного кадра - виден только первый кадр: + Result.Height := config.ReadInt('', 'frameheight', 0); + Result.Width := config.ReadInt('', 'framewidth', 0); + end; + + config.Free(); + WAD.Free(); + + FreeMem(TextureData); + FreeMem(TextData); + FreeMem(AnimWAD); end; function ShowTGATexture(ResourceStr: String): TBitMap; - var - Len: Integer; - TextureData: Pointer; - WADName, SectionName, ResourceName: String; +var + TextureData: Pointer; + WAD: TWADEditor_1; + WADName: String; + SectionName: String; + ResourceName: String; + Len: Integer; + begin Result := nil; + TextureData := nil; + Len := 0; + +// Читаем WAD: g_ProcessResourceStr(ResourceStr, WADName, SectionName, ResourceName); - g_ReadResource(WADName, SectionName, ResourceName, TextureData, Len); - if TextureData <> nil then - Result := CreateBitMap(TextureData, Len) + + WAD := TWADEditor_1.Create(); + if not WAD.ReadFile(WADName) then + begin + WAD.Free(); + Exit; + end; + +// Читаем ресурс текстуры в нем: + WAD.GetResource(utf2win(SectionName), utf2win(ResourceName), TextureData, Len); + + WAD.Free(); + +// Создаем на его основе BitMap: + Result := CreateBitMap(TextureData, Len); + + FreeMem(TextureData); end; procedure TAddTextureForm.FormActivate(Sender: TObject); diff --git a/src/editor/f_main.lfm b/src/editor/f_main.lfm index 07a325d..79d039a 100644 --- a/src/editor/f_main.lfm +++ b/src/editor/f_main.lfm @@ -746,8 +746,8 @@ object MainForm: TMainForm end end object OpenDialog: TOpenDialog - DefaultExt = '.dfz' - Filter = 'Карты Doom 2D: Forever (*.dfz, *.dfzip, *.zip, *.wad)|*.dfz;*.dfzip;*.zip;*.wad|Старые карты Doom 2D: Forever 0.30 (*.ini)|*.ini|Все файлы (*.*)|*.*' + DefaultExt = '.wad' + Filter = 'Карты Doom 2D: Forever (*.wad)|*.wad|Старые карты Doom 2D: Forever (*.ini)|*.ini|Все файлы (*.*)|*.*' Options = [ofHideReadOnly, ofNoChangeDir, ofPathMustExist, ofFileMustExist, ofEnableSizing, ofDontAddToRecent] Left = 32 Top = 64 @@ -890,8 +890,8 @@ object MainForm: TMainForm } end object SaveDialog: TSaveDialog - DefaultExt = '.dfz' - Filter = 'Карты Doom 2D: Forever (*.dfz)|*.dfz|Карты Doom 2D: Forever (*.dfzip)|*.dfzip|Карты Doom 2D: Forever (*.zip)|*.zip|Карты Doom 2D: Forever (*.wad)|*.wad|Все файлы (*.*)|*.*' + DefaultExt = '.wad' + Filter = 'Карты Doom 2D: Forever (*.wad)|*.wad|Все файлы (*.*)|*.*' Options = [ofHideReadOnly, ofNoChangeDir, ofPathMustExist, ofNoReadOnlyReturn, ofEnableSizing, ofDontAddToRecent] Left = 64 Top = 64 diff --git a/src/editor/f_main.pas b/src/editor/f_main.pas index 53bf85d..2bd2fc6 100644 --- a/src/editor/f_main.pas +++ b/src/editor/f_main.pas @@ -349,10 +349,10 @@ uses f_mapoptions, g_basic, f_about, f_mapoptimization, f_mapcheck, f_addresource_texture, g_textures, f_activationtype, f_keys, wadreader, fileutil, - MAPREADER, f_selectmap, f_savemap, WADEDITOR, MAPDEF, + MAPREADER, f_selectmap, f_savemap, WADEDITOR, WADSTRUCT, MAPDEF, g_map, f_saveminimap, f_addresource, CONFIG, f_packmap, f_addresource_sound, f_choosetype, - g_language, ClipBrd, g_resources, g_options; + g_language, ClipBrd, g_options; const UNDO_DELETE_PANEL = 1; @@ -2649,13 +2649,21 @@ var cwdt, chgt: Byte; spc: ShortInt; ID: DWORD; + wad: TWADEditor_1; cfgdata: Pointer; cfglen: Integer; config: TConfig; begin + cfgdata := nil; + cfglen := 0; ID := 0; - g_ReadResource(GameWad, 'FONTS', cfgres, cfgdata, cfglen); - if cfgdata <> nil then + + wad := TWADEditor_1.Create; + if wad.ReadFile(GameWad) then + wad.GetResource('FONTS', cfgres, cfgdata, cfglen); + wad.Free(); + + if cfglen <> 0 then begin if not g_CreateTextureWAD('FONT_STD', GameWad + ':FONTS\' + texture) then e_WriteLog('ERROR ERROR ERROR', MSG_WARNING); @@ -2666,15 +2674,14 @@ begin spc := Min(Max(config.ReadInt('FontMap', 'Kerning', 0), -128), 127); if g_GetTexture('FONT_STD', ID) then - e_TextureFontBuild(ID, FontID, cwdt, chgt, spc - 2); + e_TextureFontBuild(ID, FontID, cwdt, chgt, spc-2); config.Free(); - FreeMem(cfgdata) end else - begin - e_WriteLog('Could not load FONT_STD', MSG_WARNING) - end + e_WriteLog('Could not load FONT_STD', MSG_WARNING); + + if cfglen <> 0 then FreeMem(cfgdata); end; procedure TMainForm.FormCreate(Sender: TObject); @@ -2822,9 +2829,6 @@ begin s := config.ReadStr('Editor', 'Language', ''); gLanguage := s; - Compress := config.ReadBool('Editor', 'Compress', True); - Backup := config.ReadBool('Editor', 'Backup', True); - TestGameMode := config.ReadStr('TestRun', 'GameMode', 'DM'); TestLimTime := config.ReadStr('TestRun', 'LimTime', '0'); TestLimScore := config.ReadStr('TestRun', 'LimScore', '0'); @@ -6394,47 +6398,65 @@ end; procedure TMainForm.aDeleteMap(Sender: TObject); var - res: Integer; - FileName: String; - MapName: String; + WAD: TWADEditor_1; + MapList: SArray; + MapName: Char16; + a: Integer; + str: String; begin OpenDialog.Filter := MsgFileFilterWad; if not OpenDialog.Execute() then Exit; - FileName := OpenDialog.FileName; - SelectMapForm.Caption := MsgCapRemove; - SelectMapForm.lbMapList.Items.Clear(); - SelectMapForm.GetMaps(FileName); + WAD := TWADEditor_1.Create(); - if SelectMapForm.ShowModal() <> mrOK then + if not WAD.ReadFile(OpenDialog.FileName) then + begin + WAD.Free(); Exit; + end; - MapName := SelectMapForm.lbMapList.Items[SelectMapForm.lbMapList.ItemIndex]; - if Application.MessageBox(PChar(Format(MsgMsgDeleteMapPrompt, [MapName, OpenDialog.FileName])), PChar(MsgMsgDeleteMap), MB_ICONQUESTION or MB_YESNO or MB_DEFBUTTON2) <> mrYes then - Exit; + WAD.CreateImage(); + + MapList := WAD.GetResourcesList(''); - g_DeleteResource(FileName, '', MapName, res); - if res <> 0 then + SelectMapForm.Caption := MsgCapRemove; + SelectMapForm.lbMapList.Items.Clear(); + + if MapList <> nil then + for a := 0 to High(MapList) do + SelectMapForm.lbMapList.Items.Add(win2utf(MapList[a])); + + if (SelectMapForm.ShowModal() = mrOK) then begin - Application.MessageBox(PChar('Cant delete map res=' + IntToStr(res)), PChar('Map not deleted!'), MB_ICONINFORMATION or MB_OK or MB_DEFBUTTON1); - Exit - end; + str := SelectMapForm.lbMapList.Items[SelectMapForm.lbMapList.ItemIndex]; + MapName := ''; + Move(str[1], MapName[0], Min(16, Length(str))); + + if Application.MessageBox(PChar(Format(MsgMsgDeleteMapPrompt, [MapName, OpenDialog.FileName])), PChar(MsgMsgDeleteMap), MB_ICONQUESTION or MB_YESNO or MB_DEFBUTTON2) <> mrYes then + Exit; + + WAD.RemoveResource('', utf2win(MapName)); + + Application.MessageBox( + PChar(Format(MsgMsgMapDeletedPrompt, [MapName])), + PChar(MsgMsgMapDeleted), + MB_ICONINFORMATION or MB_OK or MB_DEFBUTTON1 + ); - Application.MessageBox( - PChar(Format(MsgMsgMapDeletedPrompt, [MapName])), - PChar(MsgMsgMapDeleted), - MB_ICONINFORMATION or MB_OK or MB_DEFBUTTON1 - ); + WAD.SaveTo(OpenDialog.FileName); // Удалили текущую карту - сохранять по старому ее нельзя: - if OpenedMap = (FileName + ':\' + MapName) then - begin - OpenedMap := ''; - OpenedWAD := ''; - MainForm.Caption := FormCaption - end + if OpenedMap = (OpenDialog.FileName+':\'+MapName) then + begin + OpenedMap := ''; + OpenedWAD := ''; + MainForm.Caption := FormCaption; + end; + end; + + WAD.Free(); end; procedure TMainForm.vleObjectPropertyKeyDown(Sender: TObject; diff --git a/src/editor/f_options.lfm b/src/editor/f_options.lfm index 0a3eca3..6ae5c8e 100644 --- a/src/editor/f_options.lfm +++ b/src/editor/f_options.lfm @@ -1,198 +1,199 @@ object OptionsForm: TOptionsForm - Left = 98 - Height = 360 + Left = 96 + Height = 401 Top = 345 - Width = 640 + Width = 713 BorderIcons = [biSystemMenu] BorderStyle = bsSingle Caption = 'Настройки редактора' - ClientHeight = 360 - ClientWidth = 640 + ClientHeight = 401 + ClientWidth = 713 Color = clBtnFace + DesignTimePPI = 107 Font.Color = clWindowText - Font.Height = -11 + Font.Height = -12 Font.Name = 'MS Sans Serif' OnCreate = FormCreate OnShow = FormShow Position = poScreenCenter LCLVersion = '2.2.4.0' object PageControl: TPageControl - Left = 8 - Height = 312 - Top = 8 - Width = 624 + Left = 9 + Height = 348 + Top = 9 + Width = 696 ActivePage = TabGeneral TabIndex = 0 TabOrder = 0 Options = [nboKeyboardTabSwitch, nboDoChangeOnSetIndex] object TabGeneral: TTabSheet Caption = 'General' - ClientHeight = 284 - ClientWidth = 620 + ClientHeight = 319 + ClientWidth = 692 object cbShowDots: TCheckBox - Left = 8 + Left = 9 Height = 21 - Top = 8 - Width = 128 + Top = 9 + Width = 135 Caption = 'Показывать сетку' TabOrder = 0 end object cbShowTexture: TCheckBox - Left = 8 + Left = 9 Height = 21 - Top = 32 - Width = 193 + Top = 36 + Width = 207 Caption = 'Показывать текстуру панели' TabOrder = 1 end object cbShowSize: TCheckBox - Left = 8 + Left = 9 Height = 21 - Top = 56 - Width = 191 + Top = 62 + Width = 204 Caption = 'Показывать размеры панели' TabOrder = 2 end object cbCheckerboard: TCheckBox - Left = 8 + Left = 9 Height = 21 - Top = 80 - Width = 164 + Top = 89 + Width = 176 Caption = 'Использовать шахматку' Checked = True State = cbChecked TabOrder = 3 end object LabelGrid: TLabel - Left = 8 - Height = 14 - Top = 112 - Width = 72 + Left = 9 + Height = 15 + Top = 125 + Width = 78 Caption = 'Шаги сетки:' ParentColor = False end object SpinEdit1: TSpinEdit - Left = 8 - Height = 22 - Top = 128 - Width = 50 + Left = 9 + Height = 23 + Top = 143 + Width = 56 MaxValue = 2048 MinValue = 4 TabOrder = 4 Value = 16 end object SpinEdit2: TSpinEdit - Left = 8 - Height = 22 - Top = 152 - Width = 50 + Left = 9 + Height = 23 + Top = 169 + Width = 56 MaxValue = 2048 MinValue = 4 TabOrder = 5 Value = 8 end object LabelGridSize: TLabel - Left = 8 - Height = 14 - Top = 184 - Width = 118 + Left = 9 + Height = 15 + Top = 205 + Width = 127 Caption = 'Размер точек сетки:' ParentColor = False WordWrap = True end object SpinEdit4: TSpinEdit - Left = 8 - Height = 22 - Top = 200 - Width = 50 + Left = 9 + Height = 23 + Top = 223 + Width = 56 MaxValue = 2 MinValue = 1 TabOrder = 6 Value = 1 end object LabelMinimap: TLabel - Left = 8 - Height = 14 - Top = 232 - Width = 128 + Left = 9 + Height = 15 + Top = 259 + Width = 139 Caption = 'Масштаб мини-карты:' ParentColor = False end object SpinEdit5: TSpinEdit - Left = 8 - Height = 22 - Top = 248 - Width = 50 + Left = 9 + Height = 23 + Top = 276 + Width = 56 MaxValue = 10 MinValue = 1 TabOrder = 7 Value = 1 end object LabelGridCol: TLabel - Left = 304 - Height = 14 - Top = 8 - Width = 68 + Left = 339 + Height = 15 + Top = 9 + Width = 74 Caption = 'Цвет сетки:' ParentColor = False end object ColorButton1: TColorButton - Left = 304 - Height = 25 - Top = 24 - Width = 75 + Left = 339 + Height = 28 + Top = 27 + Width = 84 BorderWidth = 2 ButtonColorSize = 16 ButtonColor = clRed end object LabelBack: TLabel - Left = 304 - Height = 14 - Top = 64 - Width = 65 + Left = 339 + Height = 15 + Top = 71 + Width = 70 Caption = 'Цвет фона:' ParentColor = False end object ColorButton2: TColorButton - Left = 304 - Height = 25 - Top = 80 - Width = 75 + Left = 339 + Height = 28 + Top = 89 + Width = 84 BorderWidth = 2 ButtonColorSize = 16 ButtonColor = clLime end object LabelPreview: TLabel - Left = 304 - Height = 14 - Top = 120 - Width = 248 + Left = 339 + Height = 15 + Top = 134 + Width = 270 Caption = 'Цвет фона поля предпросмотра текстуры:' ParentColor = False WordWrap = True end object ColorButton3: TColorButton - Left = 304 - Height = 25 - Top = 136 - Width = 75 + Left = 339 + Height = 28 + Top = 152 + Width = 84 BorderWidth = 2 ButtonColorSize = 16 ButtonColor = clBlue end object LabelLanguage: TLabel - Left = 304 - Height = 14 - Top = 172 + Left = 339 + Height = 15 + Top = 192 Width = 34 Caption = 'Язык:' ParentColor = False end object cbLanguage: TComboBox - Left = 304 - Height = 26 - Top = 192 - Width = 120 + Left = 339 + Height = 27 + Top = 214 + Width = 134 ItemHeight = 0 Style = csDropDownList TabOrder = 8 @@ -200,227 +201,211 @@ object OptionsForm: TOptionsForm end object TabFiles: TTabSheet Caption = 'Files' - ClientHeight = 284 - ClientWidth = 620 - object cbCompress: TCheckBox - Left = 8 - Height = 21 - Top = 8 - Width = 208 - Caption = 'Сжимать архив при сохранении' - TabOrder = 0 - end - object cbBackup: TCheckBox - Left = 8 - Height = 21 - Top = 32 - Width = 218 - Caption = 'Резервная копия при сохранении' - TabOrder = 1 - end + ClientHeight = 319 + ClientWidth = 692 object LabelRecent: TLabel - Left = 8 - Height = 14 - Top = 64 - Width = 230 + Left = 9 + Height = 15 + Top = 8 + Width = 250 Caption = 'Запоминать последних открытых карт:' ParentColor = False WordWrap = True end object SpinEdit3: TSpinEdit - Left = 8 - Height = 22 - Top = 80 - Width = 50 + Left = 9 + Height = 23 + Top = 32 + Width = 56 MaxValue = 10 MinValue = 2 - TabOrder = 2 + TabOrder = 0 Value = 2 end end object TabTesting: TTabSheet Caption = 'Testing' - ClientHeight = 284 - ClientWidth = 620 + ClientHeight = 319 + ClientWidth = 692 object LabelPath: TLabel - Left = 8 - Height = 14 - Top = 8 - Width = 120 + Left = 9 + Height = 15 + Top = 9 + Width = 131 Caption = 'Путь к Doom2DF.exe:' ParentColor = False end object ExeEdit: TFileNameEdit - Left = 8 - Height = 22 - Top = 24 - Width = 328 + Left = 9 + Height = 23 + Top = 27 + Width = 366 FileName = 'Doom2DF.exe' DialogOptions = [ofNoChangeDir, ofDontAddToRecent, ofViewDetail] FilterIndex = 0 HideDirectories = False - ButtonWidth = 23 + ButtonWidth = 26 NumGlyphs = 1 MaxLength = 0 TabOrder = 0 Text = 'Doom2DF.exe' end object LabelArgs: TLabel - Left = 8 - Height = 14 - Top = 55 - Width = 120 + Left = 9 + Height = 15 + Top = 61 + Width = 128 Caption = 'Параметры запуска:' ParentColor = False end object edD2DArgs: TEdit - Left = 8 - Height = 22 - Top = 72 - Width = 301 + Left = 9 + Height = 23 + Top = 80 + Width = 335 TabOrder = 1 end object rbDM: TRadioButton - Left = 8 + Left = 9 Height = 21 - Top = 104 - Width = 91 + Top = 116 + Width = 98 Caption = 'Deathmatch' Checked = True TabOrder = 2 TabStop = True end object rbTDM: TRadioButton - Left = 8 + Left = 9 Height = 21 - Top = 120 - Width = 124 + Top = 134 + Width = 133 Caption = 'Team Deathmatch' TabOrder = 3 end object rbCTF: TRadioButton - Left = 8 + Left = 9 Height = 21 - Top = 136 - Width = 114 + Top = 152 + Width = 124 Caption = 'Capture the Flag' TabOrder = 4 end object rbCOOP: TRadioButton - Left = 8 + Left = 9 Height = 21 - Top = 152 - Width = 92 + Top = 169 + Width = 94 Caption = 'Cooperative' TabOrder = 5 end object cbTwoPlayers: TCheckBox - Left = 168 + Left = 187 Height = 21 - Top = 104 - Width = 89 + Top = 116 + Width = 93 Caption = 'Два игрока' TabOrder = 6 end object cbTeamDamage: TCheckBox - Left = 168 + Left = 187 Height = 21 - Top = 120 - Width = 141 + Top = 134 + Width = 150 Caption = 'Урон своей команде' TabOrder = 7 end object cbAllowExit: TCheckBox - Left = 168 + Left = 187 Height = 21 - Top = 136 - Width = 122 + Top = 152 + Width = 128 Caption = 'Выход из уровня' Checked = True State = cbChecked TabOrder = 8 end object cbWeaponStay: TCheckBox - Left = 168 + Left = 187 Height = 21 - Top = 152 - Width = 125 + Top = 169 + Width = 133 Caption = 'Оружие остается' TabOrder = 9 end object cbMonstersDM: TCheckBox - Left = 168 + Left = 187 Height = 21 - Top = 168 - Width = 103 + Top = 187 + Width = 113 Caption = 'Монстры в DM' TabOrder = 10 end object LabelTime: TLabel - Left = 8 - Height = 14 - Top = 200 - Width = 92 + Left = 9 + Height = 15 + Top = 223 + Width = 103 Caption = 'Лимит времени:' ParentColor = False end object edTime: TEdit - Left = 120 - Height = 22 - Top = 200 - Width = 49 + Left = 134 + Height = 23 + Top = 223 + Width = 55 TabOrder = 11 Text = '0' end object LabelSecs: TLabel - Left = 174 - Height = 14 - Top = 200 - Width = 42 + Left = 194 + Height = 15 + Top = 223 + Width = 44 Caption = 'секунд' ParentColor = False end object LabelScore: TLabel - Left = 8 - Height = 14 - Top = 223 - Width = 76 + Left = 9 + Height = 15 + Top = 249 + Width = 84 Caption = 'Лимит очков:' ParentColor = False end object edScore: TEdit - Left = 120 - Height = 22 - Top = 223 - Width = 49 + Left = 134 + Height = 23 + Top = 249 + Width = 55 TabOrder = 12 Text = '0' end object cbMapOnce: TCheckBox - Left = 8 + Left = 9 Height = 21 - Top = 256 - Width = 241 + Top = 285 + Width = 259 Caption = 'Закрыть игру после выхода из карты' TabOrder = 13 end end end object bOK: TButton - Left = 464 - Height = 25 - Top = 328 - Width = 75 + Left = 517 + Height = 28 + Top = 366 + Width = 84 Caption = 'ОК' Default = True OnClick = bOKClick TabOrder = 1 end object bCancel: TButton - Left = 557 - Height = 25 - Top = 328 - Width = 75 + Left = 621 + Height = 28 + Top = 366 + Width = 84 Cancel = True Caption = 'Отмена' OnClick = bCancelClick diff --git a/src/editor/f_options.pas b/src/editor/f_options.pas index fe05e20..ebb38cb 100644 --- a/src/editor/f_options.pas +++ b/src/editor/f_options.pas @@ -10,16 +10,11 @@ uses ExtCtrls, ComCtrls, ActnList, Spin, EditBtn, Registry, Math, Types; type - - { TOptionsForm } - TOptionsForm = class (TForm) bOK: TButton; bCancel: TButton; cbAllowExit: TCheckBox; - cbBackup: TCheckBox; cbCheckerboard: TCheckBox; - cbCompress: TCheckBox; cbLanguage: TComboBox; cbMapOnce: TCheckBox; cbMonstersDM: TCheckBox; @@ -77,7 +72,7 @@ procedure RegisterFileType(ext: String; FileName: String); implementation uses - LazFileUtils, f_main, StdConvs, CONFIG, g_language, g_resources, g_options; + LazFileUtils, f_main, StdConvs, CONFIG, g_language, g_options; {$R *.lfm} @@ -136,8 +131,6 @@ begin end; // Files Tab: - cbCompress.Checked := Compress; - cbBackup.Checked := Backup; SpinEdit3.Value := RecentCount; // Testing Tab: @@ -194,8 +187,6 @@ begin // Files tab: re := SpinEdit3.Value; - Compress := cbCompress.Checked; - Backup := cbBackup.Checked; // Testing tab: TestD2DExe := ExeEdit.Text; @@ -240,8 +231,6 @@ begin config.WriteStr('Editor', 'Language', gLanguage); config.WriteInt('Editor', 'RecentCount', re); - config.WriteBool('Editor', 'Compress', Compress); - config.WriteBool('Editor', 'Backup', Backup); config.WriteStr('TestRun', 'GameMode', TestGameMode); config.WriteStr('TestRun', 'LimTime', TestLimTime); diff --git a/src/editor/f_packmap.lfm b/src/editor/f_packmap.lfm index 9a6db34..2a275eb 100644 --- a/src/editor/f_packmap.lfm +++ b/src/editor/f_packmap.lfm @@ -177,8 +177,8 @@ object PackMapForm: TPackMapForm TabOrder = 1 end object SaveDialog: TSaveDialog - DefaultExt = '.dfz' - Filter = 'Карты Doom 2D: Forever (*.dfz)|*.dfz|Карты Doom 2D: Forever (*.dfzip)|*.dfzip|Карты Doom 2D: Forever (*.zip)|*.zip|Карты Doom2D: Forever (*.wad)|*.wad|All files (*.*)|*.*' + DefaultExt = '.wad' + Filter = 'Карты Doom2D: Forever (*.wad)|*.wad|All files (*.*)|*.*' Options = [ofHideReadOnly, ofPathMustExist, ofEnableSizing, ofDontAddToRecent] left = 8 top = 200 diff --git a/src/editor/f_packmap.pas b/src/editor/f_packmap.pas index f06f5c8..911700b 100644 --- a/src/editor/f_packmap.pas +++ b/src/editor/f_packmap.pas @@ -53,7 +53,7 @@ implementation uses BinEditor, WADEDITOR, g_map, MAPREADER, MAPWRITER, MAPSTRUCT, - f_main, math, g_language, g_resources, g_options, e_log; + f_main, math, g_language, g_options, e_log; {$R *.lfm} @@ -70,43 +70,66 @@ begin eWAD.Text := SaveDialog.FileName; end; -function ProcessResource(wad_to, section_to, filename, section, resource: String): Boolean; - var - data: Pointer; - res, len: Integer; - us, un: String; +function ProcessResource(wad_to: TWADEditor_1; section_to, filename, section, resource: String): Boolean; +var + wad2: TWADEditor_1; + data: Pointer; + reslen: Integer; + //s: string; + begin - Result := True; + Result := False; + if filename = '' then - g_GetResourceSection(OpenedMap, filename, us, un) + g_ProcessResourceStr(OpenedMap, @filename, nil, nil) else filename := WadsDir + DirectorySeparator + filename; - e_WriteLog('ProcessResource: "' + wad_to + '" "' + section_to + '" "' + filename + '" "' + section + '" "' + resource + '"', MSG_NOTIFY); - if resource = '' then Exit; +// Читаем ресурс из WAD-файла карты или какого-то другого: + wad2 := TWADEditor_1.Create(); - g_ReadResource(filename, section, resource, data, len); - if data <> nil then + if not wad2.ReadFile(filename) then begin - (* Write resource only if it does not exists *) - g_ExistsResource(wad_to, section_to, resource, res); - if res <> 0 then - begin - g_AddResource(wad_to, section_to, resource, data, len, res); - ASSERT(res = 0) - end; - FreeMem(data); - end - else + Application.MessageBox(PChar(Format(MsgMsgWadError, [ExtractFileName(filename)])), PChar(MsgMsgError), MB_OK + MB_ICONERROR); + wad2.Free(); + Exit; + end; + + if not wad2.GetResource(utf2win(section), utf2win(resource), data, reslen) then begin - //Application.MessageBox(PChar(Format(MsgMsgWadError, [ExtractFileName(filename)])), PChar(MsgMsgError), MB_OK + MB_ICONERROR); Application.MessageBox(PChar(Format(MsgMsgResError, [filename, section, resource])), PChar(MsgMsgError), MB_OK + MB_ICONERROR); - Result := False - end + wad2.Free(); + Exit; + end; + + wad2.Free(); + + {if wad_to.HaveResource(utf2win(section_to), utf2win(resource)) then + begin + for a := 2 to 256 do + begin + s := IntToStr(a); + if not wad_to.HaveResource(utf2win(section_to), utf2win(resource+s)) then Break; + end; + resource := resource+s; + end;} + +// Если такого ресурса нет в WAD-файле-назначении, то копируем: + if not wad_to.HaveResource(utf2win(section_to), utf2win(resource)) then + begin + if not wad_to.HaveSection(utf2win(section_to)) then + wad_to.AddSection(utf2win(section_to)); + wad_to.AddResource(data, reslen, utf2win(resource), utf2win(section_to)); + end; + + FreeMem(data); + + Result := True; end; procedure TPackMapForm.bPackClick(Sender: TObject); var + WAD: TWADEditor_1; mr: TMapReader_1; mw: TMapWriter_1; data: Pointer; @@ -131,8 +154,12 @@ begin if data = nil then Exit; - if not cbAdd.Checked then - g_DeleteFile(eWAD.Text, '.bak0'); + WAD := TWADEditor_1.Create(); + +// Не перезаписывать WAD, а дополнить: + if cbAdd.Checked then + if WAD.ReadFile(eWAD.Text) then + WAD.CreateImage(); // Читаем карту из памяти: mr := TMapReader_1.Create(); @@ -150,7 +177,7 @@ begin if IsSpecialTexture(res) then Continue; - g_GetResourceSection(res, filename, section, resource); + g_ProcessResourceStr(res, @filename, @section, @resource); // Не записывать стандартные текстуры: if (not cbNonStandart.Checked) or @@ -158,9 +185,10 @@ begin (AnsiLowerCase(filename) <> SHRSHADE_WAD) ) then begin // Копируем ресурс текстуры: - if not f_packmap.ProcessResource(eWAD.Text, tsection, filename, section, resource) then + if not f_packmap.ProcessResource(WAD, tsection, filename, section, resource) then begin mr.Free(); + WAD.Free(); Exit; end; @@ -178,7 +206,7 @@ begin if cbSky.Checked then begin res := win2utf(header.SkyName); - g_GetResourceSection(res, filename, section, resource); + g_ProcessResourceStr(res, @filename, @section, @resource); // Не записывать стандартное небо: if (not cbNonStandart.Checked) or @@ -186,9 +214,10 @@ begin (AnsiLowerCase(filename) <> SHRSHADE_WAD) ) then begin // Копируем ресурс неба: - if not f_packmap.ProcessResource(eWAD.Text, ssection, filename, section, resource) then + if not f_packmap.ProcessResource(WAD, ssection, filename, section, resource) then begin mr.Free(); + WAD.Free(); Exit; end; @@ -203,7 +232,7 @@ begin if cbMusic.Checked then begin res := win2utf(header.MusicName); - g_GetResourceSection(res, filename, section, resource); + g_ProcessResourceStr(res, @filename, @section, @resource); // Не записывать стандартную музыку: if (not cbNonStandart.Checked) or @@ -211,9 +240,10 @@ begin (AnsiLowerCase(filename) <> SHRSHADE_WAD) ) then begin // Копируем ресурс музыки: - if not f_packmap.ProcessResource(eWAD.Text, msection, filename, section, resource) then + if not f_packmap.ProcessResource(WAD, msection, filename, section, resource) then begin mr.Free(); + WAD.Free(); Exit; end; @@ -263,7 +293,7 @@ begin if res = '' then Break; - g_GetResourceSection(res, @filename, @section, @resource); + g_ProcessResourceStr(res, @filename, @section, @resource); // Не записывать стандартные дополнительные текстуры: if (not cbNonStandart.Checked) or @@ -271,7 +301,7 @@ begin (AnsiLowerCase(filename) <> SHRSHADE_WAD) ) then begin // Копируем ресурс дополнительной текстуры: - if f_packmap.ProcessResource(eWAD.Text, tsection, filename, section, resource) then + if f_packmap.ProcessResource(WAD, tsection, filename, section, resource) then begin Нужно проверять есть такая текстура textures и есть ли она вообще? @@ -303,13 +333,15 @@ begin // Сохраняем карту из памяти под новым именем в WAD-файл: len := mw.SaveMap(data); - g_AddResource(eWAD.Text, '', eResource.Text, data, len, a); + WAD.AddResource(data, len, eResource.Text, ''); + WAD.SaveTo(eWAD.Text); + mw.Free(); mr.Free(); - Close(); + WAD.Free(); - ASSERT(a = 0); (* saved *) MessageDlg(Format(MsgMsgPacked, [eResource.Text, ExtractFileName(eWAD.Text)]), mtInformation, [mbOK], 0); + Close(); end; procedure TPackMapForm.FormCreate(Sender: TObject); diff --git a/src/editor/f_savemap.pas b/src/editor/f_savemap.pas index a508c24..e57fb79 100644 --- a/src/editor/f_savemap.pas +++ b/src/editor/f_savemap.pas @@ -35,7 +35,7 @@ var implementation uses - MAPREADER, MAPSTRUCT, g_language, g_resources, sfs; + BinEditor, MAPREADER, WADEDITOR, WADSTRUCT, MAPSTRUCT, g_language; {$R *.lfm} @@ -85,64 +85,67 @@ begin end; procedure TSaveMapForm.GetMaps(FileName: String; placeName: Boolean); - var - nm: String; - data: Pbyte; - list: TSFSFileList; - i, j, len, max_num: Integer; - sign: Array [0..2] of Char; +var + WAD: TWADEditor_1; + a, max_num, j: Integer; + ResList: SArray; + Data: Pointer; + Len: Integer; + Sign: Array [0..2] of Char; + nm: String; + begin lbMapList.Items.Clear(); max_num := 1; - list := SFSFileList(FileName); - if list <> nil then - begin - for i := 0 to list.Count - 1 do + WAD := TWADEditor_1.Create(); + WAD.ReadFile(FileName); + ResList := WAD.GetResourcesList(''); + + if ResList <> nil then + for a := 0 to High(ResList) do begin - g_ReadResource(FileName, win2utf(list.Files[i].path), win2utf(list.Files[i].name), data, len); + if not WAD.GetResource('', ResList[a], Data, Len) then + Continue; + + CopyMemory(@Sign[0], Data, 3); + FreeMem(Data); - if len >= 3 then + if Sign = MAP_SIGNATURE then begin - sign[0] := chr(data[0]); - sign[1] := chr(data[1]); - sign[2] := chr(data[2]); - if sign = MAP_SIGNATURE then + nm := win2utf(ResList[a]); + lbMapList.Items.Add(nm); + + if placeName then begin - nm := win2utf(list.Files[i].name); - lbMapList.Items.Add(nm); - if placeName then + nm := UpperCase(nm); + if (nm[1] = 'M') and + (nm[2] = 'A') and + (nm[3] = 'P') then begin - nm := UpperCase(nm); - if (nm[1] = 'M') and (nm[2] = 'A') and (nm[3] = 'P') then - begin - nm := Trim(Copy(nm, 4, Length(nm)-3)); - j := StrToIntDef(nm, 0); - if j >= max_num then - max_num := j + 1; - end - end - end + nm := Trim(Copy(nm, 4, Length(nm)-3)); + j := StrToIntDef(nm, 0); + if j >= max_num then + max_num := j + 1; + end; + end; end; - if len > 0 then FreeMem(data) + Sign := ''; end; - list.Destroy; - end; - + WAD.Free(); if placeName then - begin - nm := IntToStr(max_num); - if Length(nm) < 2 then - nm := '0' + nm; - eMapName.Text := 'MAP' + nm - end + begin + nm := IntToStr(max_num); + if Length(nm) < 2 then + nm := '0' + nm; + nm := 'MAP' + nm; + eMapName.Text := nm; + end else - begin - eMapName.Text := '' - end + eMapName.Text := ''; end; end. diff --git a/src/editor/f_selectmap.pas b/src/editor/f_selectmap.pas index 7474330..46f365c 100644 --- a/src/editor/f_selectmap.pas +++ b/src/editor/f_selectmap.pas @@ -32,7 +32,7 @@ var implementation uses - MAPREADER, MAPSTRUCT, g_resources, sfs; + BinEditor, MAPREADER, WADEDITOR, WADSTRUCT, MAPSTRUCT; {$R *.lfm} @@ -54,34 +54,41 @@ begin end; procedure TSelectMapForm.GetMaps(FileName: String); - var - data: PByte; - list: TSFSFileList; - sign: Array [0..2] of Char; - i, len: Integer; +var + WAD: TWADEditor_1; + a: Integer; + ResList: SArray; + Data: Pointer; + Len: Integer; + Sign: Array [0..2] of Char; + begin lbMapList.Items.Clear(); - list := SFSFileList(FileName); - if list = nil then Exit; - - for i := 0 to list.Count - 1 do + WAD := TWADEditor_1.Create(); + if not WAD.ReadFile(FileName) then begin - g_ReadResource(FileName, win2utf(list.Files[i].path), win2utf(list.Files[i].name), data, len); + WAD.Free(); + Exit; + end; + + ResList := WAD.GetResourcesList(''); - if len >= 3 then + if ResList <> nil then + for a := 0 to High(ResList) do begin - sign[0] := chr(data[0]); - sign[1] := chr(data[1]); - sign[2] := chr(data[2]); - if sign = MAP_SIGNATURE then - lbMapList.Items.Add(win2utf(list.Files[i].name)) - end; + if not WAD.GetResource('', ResList[a], Data, Len) then + Continue; - if len > 0 then FreeMem(data) - end; + CopyMemory(@Sign[0], Data, 3); + FreeMem(Data); + + if Sign = MAP_SIGNATURE then + lbMapList.Items.Add(win2utf(ResList[a])); + Sign := ''; + end; - list.Destroy + WAD.Free(); end; end. diff --git a/src/editor/g_language.pas b/src/editor/g_language.pas index 9edd887..ea1f236 100644 --- a/src/editor/g_language.pas +++ b/src/editor/g_language.pas @@ -564,8 +564,6 @@ Interface MsgLabEsLanguageAuto = 'System Default'; MsgCtrlEsFiles = 'Files'; - MsgLabEsCompress = 'Compress archive when save'; - MsgLabEsBackup = 'Make backup before save'; MsgLabPackSaveTo = 'Save to:'; MsgLabPackMapName = 'Map Resource Name:'; @@ -629,8 +627,8 @@ Interface MsgWadSpecialMap = ''; MsgWadSpecialTexs = ''; - MsgFileFilterAll = 'Doom 2D: Forever Maps (*.dfz, *.dfzip, *.zip, *.wad)|*.dfz;*.dfzip;*.zip;*.wad|Doom 2D: Forever 0.30 Maps (*.ini)|*.ini|All Files (*.*)|*.*'; - MsgFileFilterWad = 'Doom 2D: Forever Maps (*.dfz)|*.dfz|Doom 2D: Forever Maps (*.dfzip)|*.dfzip|Doom 2D: Forever Maps (*.zip)|*.zip|Doom 2D: Forever Maps (*.wad)|*.wad|All Files (*.*)|*.*'; + MsgFileFilterAll = 'Doom 2D: Forever Maps (*.wad)|*.wad|Doom 2D: Forever 0.30 Maps (*.ini)|*.ini|All Files (*.*)|*.*'; + MsgFileFilterWad = 'Doom 2D: Forever Maps (*.wad)|*.wad|All Files (*.*)|*.*'; MsgFileFilterExeMac = 'Doom 2D Forever.app|*.app|Doom 2D Forever (Unix Executable)|Doom2DF;*'; MsgFileFilterExeWin = 'Doom2DF.exe|Doom2DF.exe;*.exe'; MsgFileFilterExeUnix = 'Doom2DF|Doom2DF;*'; @@ -1175,8 +1173,6 @@ begin LabelLanguage.Caption := MsgLabEsLanguage; // TabFiles: TabFiles.Caption := MsgCtrlEsFiles; - cbCompress.Caption := MsgLabEsCompress; - cbBackup.Caption := MsgLabEsBackup; LabelRecent.Caption := MsgLabEsRecent; // TabTesting: TabTesting.Caption := MsgCtrlEsTesting; diff --git a/src/editor/g_map.pas b/src/editor/g_map.pas index 0c35d48..a93a75a 100644 --- a/src/editor/g_map.pas +++ b/src/editor/g_map.pas @@ -246,7 +246,7 @@ implementation uses BinEditor, g_textures, Dialogs, SysUtils, CONFIG, f_main, - Forms, Math, f_addresource_texture, WADEDITOR, g_language, g_resources, g_options; + Forms, Math, f_addresource_texture, WADEDITOR, g_language, g_options; const OLD_ITEM_MEDKIT_SMALL = 1; @@ -1053,6 +1053,7 @@ end; function SaveMap(Res: String): Pointer; var + WAD: TWADEditor_1; MapWriter: TMapWriter_1; textures: TTexturesRec1Array; panels: TPanelsRec1Array; @@ -1070,6 +1071,7 @@ var Len: LongWord; begin + WAD := nil; textures := nil; panels := nil; items := nil; @@ -1081,6 +1083,17 @@ begin Data := nil; Len := 0; +// Открываем WAD, если надо: + if Res <> '' then + begin + WAD := TWADEditor_1.Create(); + g_ProcessResourceStr(Res, FileName, SectionName, ResName); + if not WAD.ReadFile(FileName) then + WAD.FreeWAD(); + + WAD.CreateImage(); + end; + MapWriter := TMapWriter_1.Create(); // Сохраняем заголовок: @@ -1337,17 +1350,19 @@ begin // Записываем в WAD, если надо: if Res <> '' then - begin - g_ProcessResourceStr(Res, FileName, SectionName, ResName); - g_AddResource(FileName, SectionName, ResName, Data, Len, a); - ASSERT(a = 0); - FreeMem(Data); - Result := nil - end + begin + s := utf2win(ResName); + WAD.RemoveResource('', s); + WAD.AddResource(Data, Len, s, ''); + WAD.SaveTo(FileName); + + FreeMem(Data); + WAD.Free(); + + Result := nil; + end else - begin - Result := Data - end + Result := Data; end; procedure AddTexture(res: String; Error: Boolean); @@ -1368,6 +1383,7 @@ end; function LoadMap(Res: String): Boolean; var + WAD: TWADEditor_1; MapReader: TMapReader_1; Header: TMapHeaderRec_1; textures: TTexturesRec1Array; @@ -1407,10 +1423,24 @@ begin MainForm.lLoad.Caption := MsgLoadWad; Application.ProcessMessages(); -// Читаем ресурс карты +// Открываем WAD: + WAD := TWADEditor_1.Create(); g_ProcessResourceStr(Res, FileName, SectionName, ResName); - g_ReadResource(FileName, SectionName, ResName, pData, Len); - if pData = nil then Exit; + + if not WAD.ReadFile(FileName) then + begin + WAD.Free(); + Exit; + end; + +// Читаем ресурс карты: + if not WAD.GetResource('', utf2win(ResName), pData, Len) then + begin + WAD.Free(); + Exit; + end; + + WAD.Free(); MapReader := TMapReader_1.Create(); diff --git a/src/editor/g_resources.pas b/src/editor/g_resources.pas deleted file mode 100644 index 85a7835..0000000 --- a/src/editor/g_resources.pas +++ /dev/null @@ -1,413 +0,0 @@ -{$ASSERTIONS ON} -unit g_resources; - -interface - - (** - g_GetResourceSection - Parse path in form 'path/to/file.wad:some/section/resouce' to - wad = 'path/to/file.wad', section = 'some/section', name = 'resource' - - g_DeleteFile - Delete file if it exists. Make backup if enabled. - return true when file not exists. - - g_ReadResource - Read whole file from wad - (data <> nil) and (len > 0) when ok - use FreeMem(data) when done - - g_ReadSubResource - Read whole file from folded wad - (data <> nil) and (len > 0) when ok - use FreeMem(data) when done - - g_DeleteResource - Delete file from wad - res = 0 when ok - - g_AddResource - Add/overwrite file to wad - res = 0 when ok - - g_ExistsResource - Check that resource exists - res = 0 when ok - **) - - (* Editor options *) - var - Compress: Boolean; - Backup: Boolean; - - procedure g_GetResourceSection (path: String; out wad, section, name: String); - function g_DeleteFile(wad: String; backupPostfix: String = '.bak'): Boolean; - - procedure g_ReadResource (wad, section, name: String; out data: PByte; out len: Integer); - procedure g_ReadSubResource (wad, section0, name0, section1, name1: String; out data: PByte; out len: Integer); - procedure g_DeleteResource (wad, section, name: String; out res: Integer); - procedure g_AddResource (wad, section, name: String; const data: PByte; len: Integer; out res: Integer); - procedure g_ExistsResource (wad, section, name: String; out res: Integer); - -implementation - - uses sfs, xstreams, dfzip, utils, Classes, SysUtils, WADEDITOR, e_log; - - function NoTrailing (path: String): String; - var i: Integer; - begin - i := Length(path); - while (i > 0) and ((path[i] = '/') or (path[i] = '\')) do dec(i); - result := Copy(path, 1, i) - end; - - function g_CleanPath (path: String; sys: Boolean = False): String; - var i, len: Integer; - begin - i := 1; - result := ''; - len := Length(path); - (* drop separators at the end *) - while (len > 1) and ((path[i] = '/') or (path[i] = '\')) do dec(len); - while i <= len do - begin - while (i <= len) and (path[i] <> '/') and (path[i] <> '\') do - begin - result := result + path[i]; - inc(i) - end; - if i <= len then - if sys then - result := result + DirectorySeparator - else - result := result + '/'; - inc(i); - while (i <= len) and ((path[i] = '/') or (path[i] = '\')) do inc(i) - end; - end; - - procedure g_GetResourceSection (path: String; out wad, section, name: String); - var i, j, len: Integer; - begin - len := Length(path); - i := len; - while (i > 0) and (path[i] <> '/') and (path[i] <> '\') do dec(i); - name := Copy(path, i + 1, len); - j := i; - while (i > 0) and (path[i] <> ':') do dec(i); - section := Copy(path, i + 1, j - i - 1); - wad := Copy(path, 1, i - 1); - end; - - function g_DeleteFile (wad: String; backupPostfix: String = '.bak'): Boolean; - var newwad: String; ok: Boolean; - begin - SFSGCCollect; - SFSGCCollect; - SFSGCCollect; - ok := true; - if FileExists(wad) then - begin - if Backup then - begin - newwad := wad + backupPostfix; - if FileExists(newwad) then ok := DeleteFile(newwad); - if ok then ok := RenameFile(wad, newwad); - end - else - ok := DeleteFile(wad); - end; - result := ok; - end; - - procedure g_AddResourceToDFWAD (wad, section, name: String; const data: PByte; len: Integer; out res: Integer); - var f: TWADEditor_1; - begin - res := 1; (* error *) - section := utf2win(NoTrailing(section)); - name := utf2win(name); - ASSERT(name <> ''); - f := TWADEditor_1.Create(); - if not f.ReadFile(wad) then - begin - (* do nothing *) - end; - f.CreateImage; - f.RemoveResource(section, name); - f.AddResource(data, len, name, section); - g_DeleteFile(wad); - f.SaveTo(wad); - f.Free; - res := 0 - end; - - procedure g_AddResourceToZip (wad, section, name: String; const data: PByte; len: Integer; out res: Integer); - var - i, n, len0: Integer; - data0: PByte; - list: TSFSFileList; - tmp, path: String; - ts: TFileStream; - dir: array of TFileInfo; - ok: Boolean; - - procedure Add (name: String; data: PByte; len: Integer); - var ds: TSFSMemoryChunkStream; - begin - SetLength(dir, n + 1); - ds := TSFSMemoryChunkStream.Create(data, len, False); - dir[n] := dfzip.ZipOne(ts, name, ds, Compress); - ds.Free; - INC(n); - end; - - begin - res := 1; - wad := ExpandFileName(wad); - section := utf2win(NoTrailing(section)); - name := utf2win(name); - ASSERT(name <> ''); - list := SFSFileList(wad); - tmp := wad + '.tmp' + IntToStr(Random(100000)); - ts := TFileStream.Create(tmp, fmCreate); - n := 0; - SetLength(dir, 0); - if list <> nil then - begin - for i := 0 to list.Count - 1 do - begin - path := NoTrailing(list.Files[i].path); - if (path <> section) or (list.Files[i].name <> name) then - begin - g_ReadResource(wad, win2utf(path), win2utf(list.Files[i].name), data0, len0); - ASSERT(data0 <> nil); - if path = '' then - path := list.Files[i].name - else - path := path + '/' + list.Files[i].name; - Add(path, data0, len0); - FreeMem(data0) - end - end; - list.Destroy - end; - - if section = '' then - path := name - else - path := section + '/' + name; - Add(path, data, len); - - dfzip.writeCentralDir(ts, dir); - ts.Free; - - ok := g_DeleteFile(wad); - if not ok then e_WriteLog('Cant delete older wad [' + wad + ']', TRecordCategory.MSG_WARNING); - ok := RenameFile(tmp, wad); - if not ok then e_WriteLog('ERROR: Cant rename [' + tmp + '] -> [' + wad + ']', TRecordCategory.MSG_WARNING); - if ok then res := 0 else res := 2; - end; - - procedure g_AddResource (wad, section, name: String; const data: PByte; len: Integer; out res: Integer); - var ext: String; - begin - ASSERT(name <> ''); - res := 2; (* unknown type *) - ext := LowerCase(SysUtils.ExtractFileExt(wad)); - e_WriteLog('g_AddResource "' + wad + '" "' + section + '" "' + name + '"', MSG_NOTIFY); - if ext = '.wad' then - g_AddResourceToDFWAD(wad, section, name, data, len, res) - else - g_AddResourceToZip(wad, section, name, data, len, res) - end; - - procedure g_DeleteResourceFromDFWAD (wad, section, name: String; out res: Integer); - var f: TWADEditor_1; - begin - ASSERT(name <> ''); - res := 1; (* error *) - section := utf2win(NoTrailing(section)); - name := utf2win(name); - f := TWADEditor_1.Create; - if not f.ReadFile(wad) then - begin - f.Free; - Exit - end; - f.CreateImage; - f.RemoveResource(section, name); - g_DeleteFile(wad); - f.SaveTo(wad); - f.Free; - res := 0 (* ok *) - end; - - procedure g_DeleteResourceFromZip (wad, section, name: String; out res: Integer); - var - data0: PByte; - i, n, len0: Integer; - list: TSFSFileList; - tmp, path: String; - ts: TFileStream; - dir: array of TFileInfo; - ok: Boolean; - - procedure Add (name: String; data: PByte; len: Integer); - var ds: TSFSMemoryChunkStream; - begin - SetLength(dir, n + 1); - ds := TSFSMemoryChunkStream.Create(data, len, False); - dir[n] := dfzip.ZipOne(ts, name, ds, Compress); - ds.Free; - INC(n); - end; - - begin - res := 1; - wad := ExpandFileName(wad); - section := utf2win(NoTrailing(section)); - name := utf2win(name); - ASSERT(name <> ''); - list := SFSFileList(wad); - tmp := wad + '.tmp' + IntToStr(Random(100000)); - ts := TFileStream.Create(tmp, fmCreate); - n := 0; - SetLength(dir, 0); - if list <> nil then - begin - for i := 0 to list.Count - 1 do - begin - path := NoTrailing(list.Files[i].path); - if (path <> section) or (list.Files[i].name <> name) then - begin - g_ReadResource(wad, win2utf(path), win2utf(list.Files[i].name), data0, len0); - ASSERT(data0 <> nil); - if path = '' then - path := list.Files[i].name - else - path := path + '/' + list.Files[i].name; - Add(path, data0, len0); - FreeMem(data0) - end - end; - list.Destroy - end; - - dfzip.writeCentralDir(ts, dir); - ts.Free; - - ok := g_DeleteFile(wad); - if not ok then e_WriteLog('Cant delete older wad [' + wad + ']', TRecordCategory.MSG_WARNING); - ok := RenameFile(tmp, wad); - if not ok then e_WriteLog('ERROR: Cant rename [' + tmp + '] -> [' + wad + ']', TRecordCategory.MSG_WARNING); - if ok then res := 0 else res := 2; - end; - - procedure g_DeleteResource (wad, section, name: String; out res: Integer); - var ext: String; - begin - ASSERT(name <> ''); - res := 2; (* unknown type *) - ext := LowerCase(SysUtils.ExtractFileExt(wad)); - if ext = '.wad' then - g_DeleteResourceFromDFWAD(wad, section, name, res) - else - g_DeleteResourceFromZip(wad, section, name, res) - end; - - procedure g_ExistsResource (wad, section, name: String; out res: Integer); - var str: String; stream: TStream; - begin - res := 1; - section := utf2win(NoTrailing(section)); - name := utf2win(name); - ASSERT(name <> ''); - if SFSAddDataFileTemp(wad, TRUE) then - begin - str := SFSGetLastVirtualName(section + '\' + name); - stream := SFSFileOpen(wad + '::' + str); - if stream <> nil then - begin - res := 0; - stream.Destroy - end - end; - SFSGCCollect - end; - - procedure g_ReadResource (wad, section, name: String; out data: PByte; out len: Integer); - var stream: TStream; str: String; i: Integer; - begin - e_WriteLog('g_ReadResource: "' + wad + '" "' + section + '" "' + name + '"', MSG_NOTIFY); - section := utf2win(NoTrailing(section)); - name := utf2win(name); - data := nil; - len := 0; - //ASSERT(name <> ''); - if name = '' then Exit; (* SKY can be void *) - if SFSAddDataFileTemp(wad, TRUE) then - begin - str := SFSGetLastVirtualName(section + '/' + name); - stream := SFSFileOpen(wad + '::' + str); - if stream <> nil then - begin - len := stream.Size; - GetMem(data, len); - ASSERT(data <> nil); - //stream.ReadBuffer(data, len); (* leads to segfault *) - for i := 0 to len - 1 do - data[i] := stream.ReadByte(); - stream.Destroy - end - end; - SFSGCCollect - end; - - procedure g_ReadSubResource (wad, section0, name0, section1, name1: String; out data: PByte; out len: Integer); - var stream0, stream1: TStream; str0, str1: String; i: Integer; - begin - data := nil; - len := 0; - section0 := utf2win(NoTrailing(section0)); - name0 := utf2win(name0); - section1 := utf2win(NoTrailing(section1)); - name1 := utf2win(name1); - //ASSERT(name0 <> ''); - //ASSERT(name1 <> ''); - if (wad = '') OR (name0 = '') OR (name1 = '') then Exit; (* ??? *) - if SFSAddDataFileTemp(wad, TRUE) then - begin - str0 := SFSGetLastVirtualName(section0 + '\' + name0); - stream0 := SFSFileOpen(wad + '::' + str0); - if stream0 <> nil then - begin - if SFSAddSubDataFile(wad + '\' + str0, stream0, TRUE) then - begin - str1 := SFSGetLastVirtualName(section1 + '\' + name1); - stream1 := SFSFileOpen(wad + '\' + str0 + '::' + str1); - if stream1 <> nil then - begin - len := stream1.Size; - GetMem(data, len); - ASSERT(data <> nil); - //stream1.ReadBuffer(data, len); (* leads to segfault *) - for i := 0 to len - 1 do - data[i] := stream1.ReadByte(); - stream1.Destroy - //stream0.Destroy (* leads to memory corruption, it destroyed with stream1? *) - end - else - begin - stream0.Destroy - end - end - else - begin - stream0.Destroy - end - end - end; - SFSGCCollect - end; - -end. diff --git a/src/editor/g_textures.pas b/src/editor/g_textures.pas index 52c6169..5d59157 100644 --- a/src/editor/g_textures.pas +++ b/src/editor/g_textures.pas @@ -28,7 +28,7 @@ procedure g_DeleteAllTextures(); implementation uses - e_log, WADEDITOR, g_basic, SysUtils, g_resources; + e_log, WADEDITOR, g_basic, SysUtils; type _TTexture = record @@ -65,26 +65,32 @@ begin end; end; -function g_SimpleCreateTextureWAD (var ID: DWORD; Resource: string): Boolean; - var - TextureData: Pointer; - ResourceLength: Integer; - FileName, SectionName, ResourceName: string; +function g_SimpleCreateTextureWAD(var ID: DWORD; Resource: string): Boolean; +var + WAD: TWADEditor_1; + FileName, + SectionName, + ResourceName: string; + TextureData: Pointer; + ResourceLength: Integer; begin - Result := False; - g_ProcessResourceStr(Resource, FileName, SectionName, ResourceName); - g_ReadResource(FileName, SectionName, ResourceName, TextureData, ResourceLength); - if TextureData <> nil then - begin - if e_CreateTextureMem(TextureData, ResourceLength, ID) then - Result := True; - FreeMem(TextureData) - end + Result := False; + g_ProcessResourceStr(Resource, FileName, SectionName, ResourceName); + + WAD := TWADEditor_1.Create; + WAD.ReadFile(FileName); + + if WAD.GetResource(utf2win(SectionName), utf2win(ResourceName), TextureData, ResourceLength) then + begin + if e_CreateTextureMem(TextureData, ResourceLength, ID) then Result := True; + FreeMem(TextureData); + end else - begin - e_WriteLog(Format('Error loading texture %s', [Resource]), MSG_WARNING) - //e_WriteLog(Format('WAD Reader error: %s', [WAD.GetLastErrorStr]), MSG_WARNING); - end; + begin + e_WriteLog(Format('Error loading texture %s', [Resource]), MSG_WARNING); + e_WriteLog(Format('WAD Reader error: %s', [WAD.GetLastErrorStr]), MSG_WARNING); + end; + WAD.Destroy; end; function g_CreateTextureMemorySize(pData: Pointer; dataLen: Integer; Name: ShortString; X, Y, @@ -115,88 +121,108 @@ begin end; function g_CreateTextureWAD(TextureName: ShortString; Resource: string; flag: Byte = 0): Boolean; - var - TextureData: Pointer; - ResourceLength: Integer; - FileName, SectionName, ResourceName: string; - find_id: DWORD; +var + WAD: TWADEditor_1; + FileName, + SectionName, + ResourceName: string; + TextureData: Pointer; + find_id: DWORD; + ResourceLength: Integer; begin - find_id := FindTexture; - g_ProcessResourceStr(Resource, FileName, SectionName, ResourceName); - g_ReadResource(FileName, SectionName, ResourceName, TextureData, ResourceLength); - if TextureData <> nil then - begin - Result := e_CreateTextureMem(TextureData, ResourceLength, TexturesArray[find_id].ID); - FreeMem(TextureData); - if Result then - begin - e_GetTextureSize( - TexturesArray[find_id].ID, - @TexturesArray[find_id].Width, - @TexturesArray[find_id].Height - ); - TexturesArray[find_id].Name := TextureName; - TexturesArray[find_id].flag := flag - end - end - else - begin - e_WriteLog(Format('Error loading texture %s', [Resource]), MSG_WARNING); - //e_WriteLog(Format('WAD Reader error: %s', [WAD.GetLastErrorStr]), MSG_WARNING); - Result := False - end + g_ProcessResourceStr(Resource, FileName, SectionName, ResourceName); + + find_id := FindTexture; + + WAD := TWADEditor_1.Create; + WAD.ReadFile(FileName); + + if WAD.GetResource(utf2win(SectionName), utf2win(ResourceName), TextureData, ResourceLength) then + begin + Result := e_CreateTextureMem(TextureData, ResourceLength, TexturesArray[find_id].ID); + FreeMem(TextureData); + if Result then + begin + e_GetTextureSize(TexturesArray[find_id].ID, @TexturesArray[find_id].Width, + @TexturesArray[find_id].Height); + TexturesArray[find_id].Name := TextureName; + TexturesArray[find_id].flag := flag; + end; + end + else + begin + e_WriteLog(Format('Error loading texture %s', [Resource]), MSG_WARNING); + e_WriteLog(Format('WAD Reader error: %s', [WAD.GetLastErrorStr]), MSG_WARNING); + Result := False; + end; + WAD.Destroy; end; -function g_SimpleCreateTextureWADSize(var ID: DWORD; Resource: String; X, Y, Width, Height: Word): Boolean; - var - TextureData: Pointer; - ResourceLength: Integer; - FileName, SectionName, ResourceName: String; +function g_SimpleCreateTextureWADSize(var ID: DWORD; Resource: string; X, Y, Width, Height: Word): Boolean; +var + WAD: TWADEditor_1; + FileName, + SectionName, + ResourceName: String; + TextureData: Pointer; + ResourceLength: Integer; begin - Result := False; - g_ProcessResourceStr(Resource, FileName, SectionName, ResourceName); - g_ReadResource(FileName, SectionName, ResourceName, TextureData, ResourceLength); - if TextureData <> nil then - begin - if e_CreateTextureMemEx(TextureData, ResourceLength, ID, X, Y, Width, Height) then - Result := True; - FreeMem(TextureData) - end - else - begin - e_WriteLog(Format('Error loading texture %s', [Resource]), MSG_WARNING) - //e_WriteLog(Format('WAD Reader error: %s', [WAD.GetLastErrorStr]), MSG_WARNING) - end + Result := False; + g_ProcessResourceStr(Resource, FileName, SectionName, ResourceName); + + WAD := TWADEditor_1.Create; + WAD.ReadFile(FileName); + + if WAD.GetResource(utf2win(SectionName), utf2win(ResourceName), TextureData, ResourceLength) then + begin + if e_CreateTextureMemEx(TextureData, ResourceLength, ID, X, Y, Width, Height) then Result := True; + FreeMem(TextureData); + end + else + begin + e_WriteLog(Format('Error loading texture %s', [Resource]), MSG_WARNING); + e_WriteLog(Format('WAD Reader error: %s', [WAD.GetLastErrorStr]), MSG_WARNING); + end; + WAD.Destroy; end; -function g_CreateTextureWADSize(TextureName: ShortString; Resource: String; X, Y, Width, Height: Word; flag: Byte = 0): Boolean; - var - TextureData: Pointer; - ResourceLength: Integer; - FileName, SectionName, ResourceName: String; - find_id: DWORD; +function g_CreateTextureWADSize(TextureName: ShortString; Resource: string; + X, Y, Width, Height: Word; flag: Byte = 0): Boolean; +var + WAD: TWADEditor_1; + FileName, + SectionName, + ResourceName: String; + TextureData: Pointer; + find_id: DWORD; + ResourceLength: Integer; begin - find_id := FindTexture; - g_ProcessResourceStr(Resource, FileName, SectionName, ResourceName); - g_ReadResource(FileName, SectionName, ResourceName, TextureData, ResourceLength); - if TextureData <> nil then + g_ProcessResourceStr(Resource, FileName, SectionName, ResourceName); + + find_id := FindTexture; + + WAD := TWADEditor_1.Create; + WAD.ReadFile(FileName); + + if WAD.GetResource(utf2win(SectionName), utf2win(ResourceName), TextureData, ResourceLength) then + begin + Result := e_CreateTextureMemEx(TextureData, ResourceLength, TexturesArray[find_id].ID, X, Y, Width, Height); + FreeMem(TextureData); + if Result then begin - Result := e_CreateTextureMemEx(TextureData, ResourceLength, TexturesArray[find_id].ID, X, Y, Width, Height); - FreeMem(TextureData); - if Result then - begin - TexturesArray[find_id].Width := Width; - TexturesArray[find_id].Height := Height; - TexturesArray[find_id].Name := TextureName; - TexturesArray[find_id].flag := flag - end - end + TexturesArray[find_id].Width := Width; + TexturesArray[find_id].Height := Height; + TexturesArray[find_id].Name := TextureName; + TexturesArray[find_id].flag := flag; + end; + end else - begin - e_WriteLog(Format('Error loading texture %s', [Resource]), MSG_WARNING); - //e_WriteLog(Format('WAD Reader error: %s', [WAD.GetLastErrorStr]), MSG_WARNING); - Result := False - end + begin + e_WriteLog(Format('Error loading texture %s', [Resource]), MSG_WARNING); + e_WriteLog(Format('WAD Reader error: %s', [WAD.GetLastErrorStr]), MSG_WARNING); + Result := False; + end; + WAD.Destroy; end; function g_GetTexture(TextureName: ShortString; var ID: DWORD): Boolean; diff --git a/src/sfs/sfs.pas b/src/sfs/sfs.pas deleted file mode 100644 index 11e1045..0000000 --- a/src/sfs/sfs.pas +++ /dev/null @@ -1,1272 +0,0 @@ -(* Copyright (C) Doom 2D: Forever Developers - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3 of the License ONLY. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - *) -// streaming file system (virtual) -{$INCLUDE ../shared/a_modes.inc} -{$SCOPEDENUMS OFF} -{.$R+} -{.$DEFINE SFS_VOLDEBUG} -unit sfs; - -interface - -uses - SysUtils, Classes, Contnrs; - - -type - ESFSError = class(Exception); - - TSFSVolume = class; - - TSFSFileInfo = class - public - fOwner: TSFSVolume; // òàê, íà âñÿêèé ñëó÷àé - fPath: AnsiString; // ðàçäåëèòåëè êàòàëîãîâ -- "/"; êîðåíü íèêàê íå îáîçíà÷åí, åñëè íå ïóñòîå, îáÿçàíî çàâåðøàòüñÿ "/" - fName: AnsiString; // òîëüêî èìÿ - fSize: Int64; // unpacked - fOfs: Int64; // in VFS (many of 'em need this %-) - - constructor Create (pOwner: TSFSVolume); - destructor Destroy (); override; - - property path: AnsiString read fPath; - property name: AnsiString read fName; - property size: Int64 read fSize; // can be -1 if size is unknown - end; - - // âèðòóàëüíàÿ ôàéëîâàÿ ñèñòåìà. ÒÎËÜÊÎ ÄËß ×ÒÅÍÈß! - // òîì ÍÅ ÄÎËÆÅÍ óáèâàòüñÿ íèêàê èíà÷å, ÷åì ïðè ïîìîùè ôàáðèêè! - TSFSVolume = class - protected - fFileName: AnsiString;// îáû÷íî èìÿ îðèãèíàëüíîãî ôàéëà - fFileStream: TStream; // îáû÷íî ïîòîê äëÿ ÷òåíèÿ îðèãèíàëüíîãî ôàéëà - fFiles: TObjectList; // TSFSFileInfo èëè íàñëåäíèêè - - // ïðèøèáèòü âñå ñòðóêòóðû. - // íå äîëæíà ïàäàòü, åñëè å¸ âûçûâàþò íåñêîëüêî ðàç. - procedure Clear (); virtual; - - // âûçûâàåòñÿ èç DoDirectoryRead() äëÿ çàïîëíåíèÿ ñïèñêà ôàéëîâ. - // ñ÷èòàåòñÿ, ÷òî âñå ìàãèêè óæå ïðîâåðåíû è ôàéë òî÷íî íàø. - // fFileName, fFileStream óæå óñòàíîâëåíû, fFiles ñîçäàí, - // â í¸ì, ñêîðåå âñåãî, íèêîãî íåò. - // ïîçèöèÿ ïîòîêà -- òà, ÷òî îñòàâèëà ôàáðèêà. - // ïðè îøèáêàõ êèäàòü èñêëþ÷åíèå, òîãäà òîì áóäåò ïðèáèò ôàáðèêîé. - // ðàçäåëèòåëè ïóòåé äîëæíû áûòü òîëüêî "/", êîðíåâîé "/" äîëæåí - // áûòü îïóùåí, ïóòè (åñëè íå ïóñòûå) äîëæíû çàâåðøàòüñÿ "/"! - // fName äîëæíî ñîäåðæàòü òîëüêî èìÿ, fPath -- òîëüêî ïóòü. - // â ïðèíöèïå, îá ýòîì ïîçàáîòèòñÿ DoDirectoryRead(), íî çà÷åì - // äàâàòü åìó ëèøíþþ ðàáîòó? - procedure ReadDirectory (); virtual; abstract; - - // íàéòè ôàéë, âåðíóòü åãî èíäåêñ â fFiles. - // ýòà ïðîöåäóðà ìîæåò ìåíÿòü fFiles! - // fPath -- â ïðàâèëüíîé ôîðìå, ñ "/", êîðíåâîé "/" óáèò, ôèíàëüíûé äîáàâëåí. - // åñëè ôàéë íå íàéäåí, âåðíóòü -1. - function FindFile (const fPath, fName: AnsiString): Integer; virtual; - - // âîçâðàùàåò êîëè÷åñòâî ôàéëîâ â fFiles - function GetFileCount (): Integer; virtual; - - // âîçâðàùàåò ôàéë ñ èíäåêñîì index. - // ìîæåò âîçâðàùàòü NIL. - // íèêàêèõ ïàäåíèé íà íåïðàâèëüíûå èíäåêñû! - function GetFiles (index: Integer): TSFSFileInfo; virtual; - - public - // pSt íå îáÿçàòåëüíî çàïîìèíàòü, åñëè îí íå íóæåí. - constructor Create (const pFileName: AnsiString; pSt: TStream); virtual; - // fFileStream óíè÷òîæàòü íåëüçÿ, åñëè îí ðàâåí ïàðàìåòðó pSt êîíñòðóêòîðà. - destructor Destroy (); override; - - // âûçûâàåò ReadDirectory(). - // ýòà ïðîöåäóðà ñàìà ðàçáåð¸òñÿ ñ äóáëèêàòàìè èì¸í: ïîäîáàâëÿåò â - // êîíåö èì¸í-äóáëèêàòîâ ïîä÷¸ðêèâàíèå è äåñÿòè÷íûé íîìåð. - // òàêæå îíà íîðìàëèçóåò âèä èì¸í. - procedure DoDirectoryRead (); - - // ïðè îøèáêàõ êèäàòüñÿ èñêëþ÷åíèÿìè. - function OpenFileByIndex (const index: Integer): TStream; virtual; abstract; - - // åñëè íå ñìîãëî îòêóïîðèòü ôàéëî (èëè åù¸ ãäå îøèáëîñü), çàøâûðí¸ò èñêëþ÷åíèå. - function OpenFileEx (const fName: AnsiString): TStream; virtual; - - property FileCount: Integer read GetFileCount; // ìîæåò âåðíóòü íîëü - // ìîæåò âîçâðàùàòü NIL. - // íèêàêèõ ïàäåíèé íà íåïðàâèëüíûå èíäåêñû! - property Files [index: Integer]: TSFSFileInfo read GetFiles; - end; - - // ôàáðèêà òîìîâ. âñå SFS ïðè ñòàðòå äîáàâëÿþò ñâîè ôàáðèêè. - // áëàãîäàðÿ ýòîìó ìîæíî ñîçäàâàòü ðàçíûå âñÿêèå SFS ñòàíäàðòíûì - // âûçîâîì ñòàíäàðòíîé ïðîöåäóðû. - // ôàáðèêà ÍÅ ÄÎËÆÍÀ óáèâàòüñÿ íèêàê èíà÷å, ÷åì ïðè ïîìîùè âûçîâà - // SFSUnregisterVolumeFactory()! ýòî ãàðàíòèðóåò, ÷òî äâèæîê - // ïåðåä ðàññòðåëîì îòäàñò åé âñå å¸ òîìà. - TSFSVolumeFactory = class - public - // åñëè äîáàâëÿåì ôàéë äàííûõ ôàéë ñ èìåíåì òèïà "zip:....", òî - // SFS èçâëå÷¸ò ýòî "zip" è ïåðåäàñò â ñèþ ôóíêöèþ. - // åæåëè ôóíêöèÿ âåðí¸ò ïðàâäó, òî SFS âûçîâåò Produce äëÿ äàííîãî - // ôàéëà. åñëè íè îäíà ôàáðèêà ïðåôèêñ íå ïðèçíàåò, òî ôàéë íå îòêðîþò. - // èñïîëüçóåòñÿ äëÿ ñêèïàíèÿ àâòîäåòåêòà. - // SFS ÍÅ Ñ×ÈÒÀÅÒ ÏÐÅÔÈÊÑÎÌ ÑÒÐÎÊÓ ÊÎÐÎ×Å ÒÐ¨Õ ÑÈÌÂÎËÎÂ! - function IsMyVolumePrefix (const prefix: AnsiString): Boolean; virtual; abstract; - // ïðîâåðÿåò, ìîæåò ëè ôàáðèêà ñäåëàòü òîì äëÿ äàííîãî ôàéëà. - // st -- îòêðûòûé äëÿ ÷òåíèÿ ôàéëîâé ïîòîê. óêàçàòåëü ÷òåíèÿ ñòîèò â íà÷àëå. - // ýòîò ïîòîê íåëüçÿ çàêðûâàòü! - // prefix: òî, ÷òî áûëî ïåðåäàíî â IsMyVolumePrefix() èëè ''. - // èñêëþ÷åíèå ñ÷èòàåòñÿ îøèáêîé, âîçâðàò NIL ñ÷èòàåòñÿ îøèáêîé. - function Produce (const prefix, fileName: AnsiString; st: TStream): TSFSVolume; virtual; abstract; - // êîãäà òîì áîëüøå íå íóæåí, îí áóäåò îòäàí ôàáðèêå íà ïåðåðàáîòêó. - // äàëåå äâèæîê íå áóäåò þçàòü ñåé òîì. - procedure Recycle (vol: TSFSVolume); virtual; abstract; - end; - - // "èòåðàòîð", âîçâðàùàåìûé SFSFileList() - TSFSFileList = class - protected - fVolume: TSFSVolume; - - function GetCount (): Integer; - function GetFiles (index: Integer): TSFSFileInfo; - - public - constructor Create (const pVolume: TSFSVolume); - destructor Destroy (); override; - - property Volume: TSFSVolume read fVolume; - property Count: Integer read GetCount; - // ïðè íåïðàâèëüíîì èíäåêñå ìîë÷à âåðí¸ò NIL. - // ïðè ïðàâèëüíîì òîæå ìîæåò âåðíóòü NIL! - // î÷åíü íå ñîâåòóþ ìåíÿòü ñîäåðæèìîå ïîëó÷åííîãî êëàññà. - // êîíå÷íî, ÿ ìîã áû âîçâðàùàòü íîâóþ ñòðóêòóðó èëè íå÷òî ïîõîæåå, - // íî áëèí, åñëè òû èäèîò è íå óìååøü äàæå êîììåíòû ÷èòàòü, òî - // êàêîãî òû âîîáùå â ïðîãðàììèíã ïîëåç? - property Files [index: Integer]: TSFSFileInfo read GetFiles; default; - end; - - -procedure SFSRegisterVolumeFactory (factory: TSFSVolumeFactory); -// ýòà ôóíêöèÿ àâòîìàòè÷åñêè ïðèáü¸ò factory. -procedure SFSUnregisterVolumeFactory (factory: TSFSVolumeFactory); - -// äîáàâèòü ñáîðíèê â ïîñòîÿííûé ñïèñîê. -// åñëè ñáîðíèê ñ òàêèì èìåíåì óæå îòêðûò, òî íå îòêðûâàåò åãî ïîâòîðíî. -// íèêîãäà íå êèäàåò èñêëþ÷åíèé. -// top: äîáàâèòü â íà÷àëî ñïèñêà ïîèñêà. -// âåðí¸ò ëîæü ïðè îøèáêå. -// ñïîñîáíî îòêðûâàòü ñáîðíèêè â ñáîðíèêàõ ïðè ïîìîùè êðóòûõ èì¸í a-la: -// "zip:pack0::pack:pack1::wad2:pack2". -// â äàëüíåéøåì ñëåäóåò îáðàùàòüñÿ ê ñáîðíèêó êàê "pack2::xxx". -// èëè ìîæíî íàïèñàòü: -// "zip:pack0::pack:pack1::wad2:pack2|datafile". -// è îáðàùàòüñÿ êàê "datafile::xxx". -// "||" ïðåîáðàçóþòñÿ â ïðîñòîé "|" è ðàçäåëèòåëåì íå ñ÷èòàþòñÿ. -// ïðèíèìàåòñÿ âî âíèìàíèå òîëüêî ïîñëåäíÿÿ òðóáà. -function SFSAddDataFile (const dataFileName: AnsiString; top: Boolean=false): Boolean; - -// äîáàâèòü ñáîðíèê âðåìåííî -function SFSAddDataFileTemp (const dataFileName: AnsiString; top: Boolean=false): Boolean; - -// äîáàâèòü â ïîñòîÿííûé ñïèñîê ñáîðíèê èç ïîòîêà ds. -// åñëè âîçâðàùàåò èñòèíó, òî SFS ñòàíîâèòñÿ âëÿäåëüöåì ïîòîêà ds è ñàìà -// óãðîáèò ñåé ïîòîê ïî íåîáõîäèìîñòè. -// virtualName ñòàíîâèòñÿ èìåíåì ñáîðíèêà äëÿ îïåðàöèè îòêðûòèÿ ôàéëà òèïà -// "packfile:file.ext". -// åñëè êàêîé-íèáóäü ñáîðíèê ñ èìåíåì virtualName óæå îòêðûò, âåðí¸ò false. -// íèêîãäà íå êèäàåò èñêëþ÷åíèé. -// top: äîáàâèòü â íà÷àëî ñïèñêà ïîèñêà. -// âåðí¸ò ëîæü ïðè îøèáêå. -// îòêðûâàåò ñáîðíèê èç ïîòîêà. dataFileName -- ÂÈÐÒÓÀËÜÍÎÅ èìÿ. -// ò.å. íà ñàìîì äåëå òàêîãî ôàéëà ìîæåò è íå áûòü íà äèñêå. -function SFSAddSubDataFile (const virtualName: AnsiString; ds: TStream; top: Boolean=false): Boolean; - -// øâûðÿåòñÿ èñêëþ÷åíèÿìè. -// åñëè fName íå èìååò óêàçàíèÿ íà ôàéë äàííûõ (ýòî òî, ÷òî îòäåëåíî îò -// îñòàëüíîãî èìåíè äâîåòî÷èåì), òî èùåì ñíà÷àëà ïî âñåì çàðåãèñòðèðîâàííûì -// ôàéëàì äàííûõ, ïîòîì â òåêóùåì êàòàëîãå, ïîòîì â êàòàëîãå, îòêóäà ñòàðòîâàëè. -// åñëè íè÷åãî íå íàøëè, êèäàåì èñêëþ÷åíèå. -function SFSFileOpenEx (const fName: AnsiString): TStream; - -// ïðè îøèáêå -- NIL, è íèêàêèõ èñêëþ÷åíèé. -function SFSFileOpen (const fName: AnsiString): TStream; - -// âîçâðàùàåò NIL ïðè îøèáêå. -// ïîñëå èñïîëüçîâàíèÿ, íàòóðàëüíî, èòåðàòîð íàäî ãðîõíóòü %-) -function SFSFileList (const dataFileName: AnsiString): TSFSFileList; - -// çàïðåòèòü îñâîáîæäåíèå âðåìåííûõ òîìîâ (ìîæíî âûçûâàòü ðåêóðñèâíî) -procedure sfsGCDisable (); - -// ðàçðåøèòü îñâîáîæäåíèå âðåìåííûõ òîìîâ (ìîæíî âûçûâàòü ðåêóðñèâíî) -procedure sfsGCEnable (); - -// for completeness sake -procedure sfsGCCollect (); - -function SFSReplacePathDelims (const s: AnsiString; newDelim: Char): AnsiString; - -// ðàçîáðàòü òîëñòîå èìÿ ôàéëà, âåðíóòü âèðòóàëüíîå èìÿ ïîñëåäíåãî ñïèñêà -// èëè ïóñòóþ ñòîðîêó, åñëè ñïèñêîâ íå áûëî. -function SFSGetLastVirtualName (const fn: AnsiString): AnsiString; - -// Wildcard matching -// this code is meant to allow wildcard pattern matches. tt is VERY useful -// for matching filename wildcard patterns. tt allows unix grep-like pattern -// comparisons, for instance: -// -// ? Matches any single characer -// + Matches any single characer or nothing -// * Matches any number of contiguous characters -// [abc] Matches a or b or c at that position -// [!abc] Matches anything but a or b or c at that position -// [a-e] Matches a through e at that position -// -// 'ma?ch.*' -Would match match.exe, mavch.dat, march.on, etc -// 'this [e-n]s a [!zy]est' -Would match 'this is a test', but would -// not match 'this as a yest' -// -function WildMatch (pattern, text: AnsiString): Boolean; -function WildListMatch (wildList, text: AnsiString; delimChar: AnsiChar=':'): Integer; -function HasWildcards (const pattern: AnsiString): Boolean; - - -var - // ïðàâäà: ðàçðåøåíî èñêàòü ôàéëî íå òîëüêî â ôàéëàõ äàííûõ, íî è íà äèñêå. - sfsDiskEnabled: Boolean = true; - // ïðàâäà: åñëè ôàéë íå ïðåôèêñîâàí, òî ñíà÷àëà èùåì ôàéëî íà äèñêå, - // ïîòîì â ôàéëàõ äàííûõ. - sfsDiskFirst: Boolean = true; - // ïðàâäà: äàæå äëÿ ïðåôèêñîâàíûõ ôàéëîâ ñíà÷àëà ïðîñìîòðèì äèñê - // (åñëè óñòàíîâëåí ôëàæîê sfsDiskFirst è sfsDiskEnabled). - sfsForceDiskForPrefixed: Boolean = false; - // ñïèñîê äèñêîâûõ êàòàëîãîâ äëÿ ïîèñêà ôàéëà. åñëè ïóñò -- èùåì òîëüêî â - // òåêóùåì. êàòàëîãè ðàçäåëÿþòñÿ òðóáîé ("|"). - // çàìåíÿåòñÿ íà òåêóùèé êàòàëîã (ñ çàâåðøàþùèì "/"), - // çàìåíÿåòñÿ íà êàòàëîã, ãäå ñèäèò .EXE (ñ çàâåðøàþùèì "/"). - sfsDiskDirs: AnsiString = '|'; - - -implementation - -uses - xstreams, utils; - - -const - // character defines - WILD_CHAR_ESCAPE = '\'; - WILD_CHAR_SINGLE = '?'; - WILD_CHAR_SINGLE_OR_NONE = '+'; - WILD_CHAR_MULTI = '*'; - WILD_CHAR_RANGE_OPEN = '['; - WILD_CHAR_RANGE = '-'; - WILD_CHAR_RANGE_CLOSE = ']'; - WILD_CHAR_RANGE_NOT = '!'; - - -function HasWildcards (const pattern: AnsiString): Boolean; -begin - result := - (Pos(WILD_CHAR_ESCAPE, pattern) <> 0) or - (Pos(WILD_CHAR_SINGLE, pattern) <> 0) or - (Pos(WILD_CHAR_SINGLE_OR_NONE, pattern) <> 0) or - (Pos(WILD_CHAR_MULTI, pattern) <> 0) or - (Pos(WILD_CHAR_RANGE_OPEN, pattern) <> 0); -end; - -function MatchMask (const pattern: AnsiString; p, pend: Integer; const text: AnsiString; t, tend: Integer): Boolean; -var - rangeStart, rangeEnd: AnsiChar; - rangeNot, rangeMatched: Boolean; - ch: AnsiChar; -begin - // sanity checks - if (pend < 0) or (pend > Length(pattern)) then pend := Length(pattern); - if (tend < 0) or (tend > Length(text)) then tend := Length(text); - if t < 1 then t := 1; - if p < 1 then p := 1; - while p <= pend do - begin - if t > tend then - begin - // no more text. check if there's no more chars in pattern (except "*" & "+") - while (p <= pend) and - ((pattern[p] = WILD_CHAR_MULTI) or - (pattern[p] = WILD_CHAR_SINGLE_OR_NONE)) do Inc(p); - result := (p > pend); - exit; - end; - case pattern[p] of - WILD_CHAR_SINGLE: ; - WILD_CHAR_ESCAPE: - begin - Inc(p); - if p > pend then result := false else result := (pattern[p] = text[t]); - if not result then exit; - end; - WILD_CHAR_RANGE_OPEN: - begin - result := false; - Inc(p); if p > pend then exit; // sanity check - rangeNot := (pattern[p] = WILD_CHAR_RANGE_NOT); - if rangeNot then begin Inc(p); if p > pend then exit; {sanity check} end; - if pattern[p] = WILD_CHAR_RANGE_CLOSE then exit; // sanity check - ch := text[t]; // speed reasons - rangeMatched := false; - repeat - if p > pend then exit; // sanity check - rangeStart := pattern[p]; - if rangeStart = WILD_CHAR_RANGE_CLOSE then break; - Inc(p); if p > pend then exit; // sanity check - if pattern[p] = WILD_CHAR_RANGE then - begin - Inc(p); if p > pend then exit; // sanity check - rangeEnd := pattern[p]; Inc(p); - if rangeStart < rangeEnd then - begin - rangeMatched := (ch >= rangeStart) and (ch <= rangeEnd); - end - else rangeMatched := (ch >= rangeEnd) and (ch <= rangeStart); - end - else rangeMatched := (ch = rangeStart); - until rangeMatched; - if rangeNot = rangeMatched then exit; - - // skip the rest or the range - while (p <= pend) and (pattern[p] <> WILD_CHAR_RANGE_CLOSE) do Inc(p); - if p > pend then exit; // sanity check - end; - WILD_CHAR_SINGLE_OR_NONE: - begin - Inc(p); - result := MatchMask(pattern, p, pend, text, t, tend); - if not result then result := MatchMask(pattern, p, pend, text, t+1, tend); - exit; - end; - WILD_CHAR_MULTI: - begin - while (p <= pend) and (pattern[p] = WILD_CHAR_MULTI) do Inc(p); - result := (p > pend); if result then exit; - while not result and (t <= tend) do - begin - result := MatchMask(pattern, p, pend, text, t, tend); - Inc(t); - end; - exit; - end; - else result := (pattern[p] = text[t]); if not result then exit; - end; - Inc(p); Inc(t); - end; - result := (t > tend); -end; - - -function WildMatch (pattern, text: AnsiString): Boolean; -begin - if pattern <> '' then pattern := AnsiLowerCase(pattern); - if text <> '' then text := AnsiLowerCase(text); - result := MatchMask(pattern, 1, -1, text, 1, -1); -end; - -function WildListMatch (wildList, text: AnsiString; delimChar: AnsiChar=':'): Integer; -var - s, e: Integer; -begin - if wildList <> '' then wildList := AnsiLowerCase(wildList); - if text <> '' then text := AnsiLowerCase(text); - result := 0; - s := 1; - while s <= Length(wildList) do - begin - e := s; while e <= Length(wildList) do - begin - if wildList[e] = WILD_CHAR_RANGE_OPEN then - begin - while (e <= Length(wildList)) and (wildList[e] <> WILD_CHAR_RANGE_CLOSE) do Inc(e); - end; - if wildList[e] = delimChar then break; - Inc(e); - end; - if s < e then - begin - if MatchMask(wildList, s, e-1, text, 1, -1) then exit; - end; - Inc(result); - s := e+1; - end; - result := -1; -end; - - -type - TVolumeInfo = class - public - fFactory: TSFSVolumeFactory; - fVolume: TSFSVolume; - fPackName: AnsiString; // äëÿ îäíîãî è òîãî æå ôàéëà áóäåò òîëüêî îäèí òîì! - fStream: TStream; // ôàéëîâûé ïîòîê äëÿ ñáîðíèêà - fPermanent: Boolean; // èñòèíà -- íå áóäåò óãðîáëåíà, åñëè íå îñòàíåòñÿ íè îäíîãî îòêðûòîãî òîìà - // èñòèíà -- ýòîò òîì áûë ñîçäàí èç ïîòîêà è íå èìååò äèñêîâîãî ôàéëà, ïîòîìó ôàáðèêå áóäåò ïåðåäàíî íå èìÿ ñáîðíèêà, à ïóñòàÿ ñòðîêà - fNoDiskFile: Boolean; - fOpenedFilesCount: Integer; - - destructor Destroy (); override; - end; - - TOwnedPartialStream = class (TSFSPartialStream) - protected - fOwner: TVolumeInfo; - - public - constructor Create (pOwner: TVolumeInfo; pSrc: TStream; pPos, pSize: Int64; pKillSrc: Boolean); - destructor Destroy (); override; - end; - - -var - factories: TObjectList; // TSFSVolumeFactory - volumes: TObjectList; // TVolumeInfo - gcdisabled: Integer = 0; // >0: disabled - - -procedure sfsGCCollect (); -var - f, c: Integer; - vi: TVolumeInfo; - used: Boolean; -begin - // collect garbage - f := 0; - while f < volumes.Count do - begin - vi := TVolumeInfo(volumes[f]); - if (vi <> nil) and (not vi.fPermanent) and (vi.fOpenedFilesCount = 0) then - begin - // this volume probably can be removed - used := false; - c := volumes.Count-1; - while not used and (c >= 0) do - begin - if (c <> f) and (volumes[c] <> nil) then - begin - used := (TVolumeInfo(volumes[c]).fStream = vi.fStream); - if not used then used := (TVolumeInfo(volumes[c]).fVolume.fFileStream = vi.fStream); - if used then break; - end; - Dec(c); - end; - if not used then - begin - {$IFDEF SFS_VOLDEBUG}writeln('000: destroying volume "', TVolumeInfo(volumes[f]).fPackName, '"');{$ENDIF} - volumes.extract(vi); // remove from list - vi.Free; // and kill - f := 0; - continue; - end; - end; - Inc(f); // next volume - end; -end; - -procedure sfsGCDisable (); -begin - Inc(gcdisabled); -end; - -procedure sfsGCEnable (); -begin - Dec(gcdisabled); - if gcdisabled <= 0 then - begin - gcdisabled := 0; - sfsGCCollect(); - end; -end; - - -// ðàçáèòü èìÿ ôàéëà íà ÷àñòè: ïðåôèêñ ôàéëîâîé ñèñòåìû, èìÿ ôàéëà äàííûõ, -// ñîáñòâåííî èìÿ ôàéëà -// èìÿ âûãëÿäèò êàê: -// (("sfspfx:")?"datafile::")*"filename" -procedure SplitFName (const fn: AnsiString; out dataFile, fileName: AnsiString); -var - f: Integer; -begin - f := Length(fn)-1; - while f >= 1 do - begin - if (fn[f] = ':') and (fn[f+1] = ':') then break; - Dec(f); - end; - if f < 1 then begin dataFile := ''; fileName := fn; end - else - begin - dataFile := Copy(fn, 1, f-1); - fileName := Copy(fn, f+2, maxInt-10000); - end; -end; - -// ñàéäýôôåêò: âûðåçàåò âèðòóàëüíîå èìÿ èç dataFile. -function ExtractVirtName (var dataFile: AnsiString): AnsiString; -var - f: Integer; -begin - f := Length(dataFile); result := dataFile; - while f > 1 do - begin - if dataFile[f] = ':' then break; - if dataFile[f] = '|' then - begin - if dataFile[f-1] = '|' then begin Dec(f); Delete(dataFile, f, 1); end - else - begin - result := Copy(dataFile, f+1, Length(dataFile)); - Delete(dataFile, f, Length(dataFile)); - break; - end; - end; - Dec(f); - end; -end; - -// ðàçáèòü èìÿ ñáîðíèêà íà ÷àñòè: ïðåôèêñ ôàéëîâîé ñèñòåìû, èìÿ ôàéëà äàííûõ, -// âèðòóàëüíîå èìÿ. åñëè âèðòóàëüíîãî èìåíè íå äàíî, îíî áóäåò ðàâíî dataFile. -// èìÿ âûãëÿäèò êàê: -// [sfspfx:]datafile[|virtname] -// åñëè ïåðåä äâîåòî÷èåì ìåíüøå òð¸õ áóêâ, òî ýòî ñ÷èòàåòñÿ íå ïðåôèêñîì, -// à èìåíåì äèñêà. -procedure SplitDataName (const fn: AnsiString; out pfx, dataFile, virtName: AnsiString); -var - f: Integer; -begin - f := Pos(':', fn); - if f <= 3 then begin pfx := ''; dataFile := fn; end - else - begin - pfx := Copy(fn, 1, f-1); - dataFile := Copy(fn, f+1, maxInt-10000); - end; - virtName := ExtractVirtName(dataFile); -end; - -// íàéòè ïðîèçâîäèòåëÿ äëÿ ýòîãî ôàéëà (åñëè ôàéë óæå îòêðûò). -// onlyPerm: òîëüêî "ïîñòîÿííûå" ïðîèçâîäèòåëè. -function FindVolumeInfo (const dataFileName: AnsiString; onlyPerm: Boolean=false): Integer; -var - f: Integer; - vi: TVolumeInfo; -begin - f := 0; - while f < volumes.Count do - begin - if volumes[f] <> nil then - begin - vi := TVolumeInfo(volumes[f]); - if not onlyPerm or vi.fPermanent then - begin - if StrEquCI1251(vi.fPackName, dataFileName) then - begin - result := f; - exit; - end; - end; - end; - Inc(f); - end; - result := -1; -end; - -// íàéòè èíôó äëÿ ýòîãî òîìà. -// õîðîøåå èìÿ, ïðàâäà? %-) -function FindVolumeInfoByVolumeInstance (vol: TSFSVolume): Integer; -begin - result := volumes.Count-1; - while result >= 0 do - begin - if volumes[result] <> nil then - begin - if TVolumeInfo(volumes[result]).fVolume = vol then exit; - end; - Dec(result); - end; -end; - - -// adds '/' too -function normalizePath (fn: AnsiString): AnsiString; -var - i: Integer; -begin - result := ''; - i := 1; - while i <= length(fn) do - begin - if (fn[i] = '.') and ((length(fn)-i = 0) or (fn[i+1] = '/') or (fn[i+1] = '\')) then - begin - i := i+2; - continue; - end; - if (fn[i] = '/') or (fn[i] = '\') then - begin - if (length(result) > 0) and (result[length(result)] <> '/') then result := result+'/'; - end - else - begin - result := result+fn[i]; - end; - Inc(i); - end; - if (length(result) > 0) and (result[length(result)] <> '/') then result := result+'/'; -end; - -function SFSReplacePathDelims (const s: AnsiString; newDelim: Char): AnsiString; -var - f: Integer; -begin - result := s; - for f := 1 to Length(result) do - begin - if (result[f] = '/') or (result[f] = '\') then - begin - // avoid unnecessary string changes - if result[f] <> newDelim then result[f] := newDelim; - end; - end; -end; - -function SFSGetLastVirtualName (const fn: AnsiString): AnsiString; -var - rest, tmp: AnsiString; - f: Integer; -begin - rest := fn; - repeat - f := Pos('::', rest); if f = 0 then f := Length(rest)+1; - tmp := Copy(rest, 1, f-1); Delete(rest, 1, f+1); - result := ExtractVirtName(tmp); - until rest = ''; -end; - - -{ TVolumeInfo } -destructor TVolumeInfo.Destroy (); -var - f, me: Integer; - used: Boolean; // ôëàæîê çàþçàíîñòè ïîòîêà êåì-òî åù¸ -begin - if fFactory <> nil then fFactory.Recycle(fVolume); - used := false; - fVolume := nil; - fFactory := nil; - fPackName := ''; - - // òèïà ìóñîðîñáîðíèê: åñëè íàø ïîòîê áîëåå íèêåì íå þçàåòñÿ, òî óãðîáèòü åãî íàôèã - if not used then - begin - me := volumes.IndexOf(self); - f := volumes.Count-1; - while not used and (f >= 0) do - begin - if (f <> me) and (volumes[f] <> nil) then - begin - used := (TVolumeInfo(volumes[f]).fStream = fStream); - if not used then - begin - used := (TVolumeInfo(volumes[f]).fVolume.fFileStream = fStream); - end; - if used then break; - end; - Dec(f); - end; - end; - if not used then FreeAndNil(fStream); // åñëè áîëüøå íèêåì íå þçàíî, ïðèøèá¸ì - inherited Destroy(); -end; - - -{ TOwnedPartialStream } -constructor TOwnedPartialStream.Create (pOwner: TVolumeInfo; pSrc: TStream; - pPos, pSize: Int64; pKillSrc: Boolean); -begin - inherited Create(pSrc, pPos, pSize, pKillSrc); - fOwner := pOwner; - if pOwner <> nil then Inc(pOwner.fOpenedFilesCount); -end; - -destructor TOwnedPartialStream.Destroy (); -var - f: Integer; -begin - inherited Destroy(); - if fOwner <> nil then - begin - Dec(fOwner.fOpenedFilesCount); - if (gcdisabled = 0) and not fOwner.fPermanent and (fOwner.fOpenedFilesCount < 1) then - begin - f := volumes.IndexOf(fOwner); - if f <> -1 then - begin - {$IFDEF SFS_VOLDEBUG}writeln('001: destroying volume "', TVolumeInfo(volumes[f]).fPackName, '"');{$ENDIF} - volumes[f] := nil; // this will destroy the volume - end; - end; - end; -end; - - -{ TSFSFileInfo } -constructor TSFSFileInfo.Create (pOwner: TSFSVolume); -begin - inherited Create(); - fOwner := pOwner; - fPath := ''; - fName := ''; - fSize := 0; - fOfs := 0; - if pOwner <> nil then pOwner.fFiles.Add(self); -end; - -destructor TSFSFileInfo.Destroy (); -begin - if fOwner <> nil then fOwner.fFiles.Extract(self); - inherited Destroy(); -end; - - -{ TSFSVolume } -constructor TSFSVolume.Create (const pFileName: AnsiString; pSt: TStream); -begin - inherited Create(); - fFileStream := pSt; - fFileName := pFileName; - fFiles := TObjectList.Create(true); -end; - -procedure TSFSVolume.DoDirectoryRead (); -var - f, c: Integer; - sfi: TSFSFileInfo; - tmp: AnsiString; -begin - fFileName := ExpandFileName(SFSReplacePathDelims(fFileName, '/')); - ReadDirectory(); - fFiles.Pack(); - - f := 0; - while f < fFiles.Count do - begin - sfi := TSFSFileInfo(fFiles[f]); - // normalize name & path - sfi.fPath := SFSReplacePathDelims(sfi.fPath, '/'); - if (sfi.fPath <> '') and (sfi.fPath[1] = '/') then Delete(sfi.fPath, 1, 1); - if (sfi.fPath <> '') and (sfi.fPath[Length(sfi.fPath)] <> '/') then sfi.fPath := sfi.fPath+'/'; - tmp := SFSReplacePathDelims(sfi.fName, '/'); - c := Length(tmp); while (c > 0) and (tmp[c] <> '/') do Dec(c); - if c > 0 then - begin - // split path and name - Delete(sfi.fName, 1, c); // cut name - tmp := Copy(tmp, 1, c); // get path - if tmp = '/' then tmp := ''; // just delimiter; ignore it - sfi.fPath := sfi.fPath+tmp; - end; - sfi.fPath := normalizePath(sfi.fPath); - if (length(sfi.fPath) = 0) and (length(sfi.fName) = 0) then sfi.Free else Inc(f); - end; -end; - -destructor TSFSVolume.Destroy (); -begin - Clear(); - FreeAndNil(fFiles); - inherited Destroy(); -end; - -procedure TSFSVolume.Clear (); -begin - fFiles.Clear(); -end; - -function TSFSVolume.FindFile (const fPath, fName: AnsiString): Integer; -begin - if fFiles = nil then result := -1 - else - begin - result := fFiles.Count; - while result > 0 do - begin - Dec(result); - if fFiles[result] <> nil then - begin - if StrEquCI1251(fPath, TSFSFileInfo(fFiles[result]).fPath) and - StrEquCI1251(fName, TSFSFileInfo(fFiles[result]).fName) then exit; - end; - end; - result := -1; - end; -end; - -function TSFSVolume.GetFileCount (): Integer; -begin - if fFiles = nil then result := 0 else result := fFiles.Count; -end; - -function TSFSVolume.GetFiles (index: Integer): TSFSFileInfo; -begin - if fFiles = nil then result := nil - else - begin - if (index < 0) or (index >= fFiles.Count) then result := nil - else result := TSFSFileInfo(fFiles[index]); - end; -end; - -function TSFSVolume.OpenFileEx (const fName: AnsiString): TStream; -var - fp, fn: AnsiString; - f, ls: Integer; -begin - fp := fName; - // normalize name, find split position - if (fp <> '') and ((fp[1] = '/') or (fp[1] = '\')) then Delete(fp, 1, 1); - ls := 0; - for f := 1 to Length(fp) do - begin - if fp[f] = '\' then fp[f] := '/'; - if fp[f] = '/' then ls := f; - end; - fn := Copy(fp, ls+1, Length(fp)); - fp := Copy(fp, 1, ls); - f := FindFile(fp, fn); - if f = -1 then raise ESFSError.Create('file not found: "'+fName+'"'); - result := OpenFileByIndex(f); - if result = nil then raise ESFSError.Create('file not found: "'+fName+'"'); -end; - - -{ TSFSFileList } -constructor TSFSFileList.Create (const pVolume: TSFSVolume); -var - f: Integer; -begin - inherited Create(); - ASSERT(pVolume <> nil); - f := FindVolumeInfoByVolumeInstance(pVolume); - ASSERT(f <> -1); - fVolume := pVolume; - Inc(TVolumeInfo(volumes[f]).fOpenedFilesCount); // íå ïîçâîëèì óáèòü çàïèñü! -end; - -destructor TSFSFileList.Destroy (); -var - f: Integer; -begin - f := FindVolumeInfoByVolumeInstance(fVolume); - ASSERT(f <> -1); - Dec(TVolumeInfo(volumes[f]).fOpenedFilesCount); - // óáü¸ì çàïèñü, åñëè îíà âðåìåííàÿ, è â íåé íåò áîëüøå íè÷åãî îòêðûòîãî - if (gcdisabled = 0) and not TVolumeInfo(volumes[f]).fPermanent and (TVolumeInfo(volumes[f]).fOpenedFilesCount < 1) then - begin - {$IFDEF SFS_VOLDEBUG}writeln('002: destroying volume "', TVolumeInfo(volumes[f]).fPackName, '"');{$ENDIF} - volumes[f] := nil; - end; - inherited Destroy(); -end; - -function TSFSFileList.GetCount (): Integer; -begin - result := fVolume.fFiles.Count; -end; - -function TSFSFileList.GetFiles (index: Integer): TSFSFileInfo; -begin - if (index < 0) or (index >= fVolume.fFiles.Count) then result := nil - else result := TSFSFileInfo(fVolume.fFiles[index]); -end; - - -procedure SFSRegisterVolumeFactory (factory: TSFSVolumeFactory); -var - f: Integer; -begin - if factory = nil then exit; - if factories.IndexOf(factory) <> -1 then - raise ESFSError.Create('duplicate factories are not allowed'); - f := factories.IndexOf(nil); - if f = -1 then factories.Add(factory) else factories[f] := factory; -end; - -procedure SFSUnregisterVolumeFactory (factory: TSFSVolumeFactory); -var - f: Integer; - c: Integer; -begin - if factory = nil then exit; - f := factories.IndexOf(factory); - if f = -1 then raise ESFSError.Create('can''t unregister nonexisting factory'); - c := 0; while c < volumes.Count do - begin - if (volumes[c] <> nil) and (TVolumeInfo(volumes[c]).fFactory = factory) then volumes[c] := nil; - Inc(c); - end; - factories[f] := nil; -end; - - -function SFSAddDataFileEx (dataFileName: AnsiString; ds: TStream; top, permanent: Integer): Integer; -// dataFileName ìîæåò èìåòü ïðåôèêñ òèïà "zip:" (ñì. âûøå: IsMyPrefix). -// ìîæåò âûêèíóòü èñêëþ÷åíèå! -// top: -// <0: äîáàâèòü â íà÷àëî ñïèñêà ïîèñêà. -// =0: íå ìåíÿòü. -// >0: äîáàâèòü â êîíåö ñïèñêà ïîèñêà. -// permanent: -// <0: ñîçäàòü "âðåìåííûé" òîì. -// =0: íå ìåíÿòü ôëàæîê ïîñòîÿíñòâà. -// >0: ñîçäàòü "ïîñòîÿííûé" òîì. -// åñëè ds <> nil, òî ñîçäà¸ò ñáîðíèê èç ïîòîêà. åñëè ñáîðíèê ñ èìåíåì -// dataFileName óæå çàðåãèñòðèðîâàí, òî ïàäàåò íàôèã. -// âîçâðàùàåò èíäåêñ â volumes. -// óìååò äåëàòü ðåêóðñèþ. -var - fac: TSFSVolumeFactory; - vol: TSFSVolume; - vi: TVolumeInfo; - f: Integer; - st, st1: TStream; - pfx: AnsiString; - fn, vfn, tmp: AnsiString; -begin - f := Pos('::', dataFileName); - if f <> 0 then - begin - // ðåêóðñèâíîå îòêðûòèå. - // ðàçîáü¸ì dataFileName íà èìÿ ñáîðíèêà è îñòàòîê. - // pfx áóäåò èìåíåì ñáîðíèêà, dataFileName -- îñòàòêîì. - pfx := Copy(dataFileName, 1, f-1); Delete(dataFileName, 1, f+1); - // ñíà÷àëà îòêðîåì ïåðâûé ñïèñîê... - result := SFSAddDataFileEx(pfx, ds, 0, 0); - // ...òåïåðü ïðîäîëæèì ñ îñòàòêîì. - // óçíàåì, êàêîå ôàéëî îòêðûâàòü. - // âûêîâûðÿåì ïåðâûé "::" ïðåôèêñ (ýòî áóäåò èìÿ ôàéëà). - f := Pos('::', dataFileName); if f = 0 then f := Length(dataFileName)+1; - fn := Copy(dataFileName, 1, f-1); Delete(dataFileName, 1, f-1); - // dataFileName õðàíèò îñòàòîê. - // èçâëå÷¸ì èìÿ ôàéëà: - SplitDataName(fn, pfx, tmp, vfn); - // îòêðîåì ýòîò ôàéë - vi := TVolumeInfo(volumes[result]); st := nil; - try - st := vi.fVolume.OpenFileEx(tmp); - st1 := TOwnedPartialStream.Create(vi, st, 0, st.Size, true); - except - FreeAndNil(st); - // óäàëèì íåèñïîëüçóåìûé âðåìåííûé òîì. - if (gcdisabled = 0) and not vi.fPermanent and (vi.fOpenedFilesCount < 1) then volumes[result] := nil; - raise; - end; - // óðà. îòêðûëè ôàéë. êèäàåì â âîçäóõ ÷åï÷èêè, ïðîäîëæàåì ðàçâëå÷åíèå. - fn := fn+dataFileName; - try - st1.Position := 0; - result := SFSAddDataFileEx(fn, st1, top, permanent); - except - st1.Free(); // à âîò íå çàëàäèëîñü. çàêðûëè îòêðûòîå ôàéëî, âûëåòåëè. - raise; - end; - exit; - end; - - // îáûêíîâåííîå íåðåêóðñèâíîå îòêðûòèå. - SplitDataName(dataFileName, pfx, fn, vfn); - - f := FindVolumeInfo(vfn); - if f <> -1 then - begin - if ds <> nil then raise ESFSError.Create('subdata name conflict'); - if permanent <> 0 then TVolumeInfo(volumes[f]).fPermanent := (permanent > 0); - if top = 0 then result := f - else if top < 0 then result := 0 - else result := volumes.Count-1; - if result <> f then volumes.Move(f, result); - exit; - end; - - if ds <> nil then st := ds - else st := TFileStream.Create(fn, fmOpenRead or {fmShareDenyWrite}fmShareDenyNone); - st.Position := 0; - - volumes.Pack(); - - fac := nil; vol := nil; - try - for f := 0 to factories.Count-1 do - begin - fac := TSFSVolumeFactory(factories[f]); - if fac = nil then continue; - if (pfx <> '') and not fac.IsMyVolumePrefix(pfx) then continue; - st.Position := 0; - try - if ds <> nil then vol := fac.Produce(pfx, '', st) - else vol := fac.Produce(pfx, fn, st); - except - vol := nil; - end; - if vol <> nil then break; - end; - if vol = nil then raise ESFSError.Create('no factory for "'+dataFileName+'"'); - except - if st <> ds then st.Free(); - raise; - end; - - vi := TVolumeInfo.Create(); - try - if top < 0 then - begin - result := 0; - volumes.Insert(0, vi); - end - else result := volumes.Add(vi); - except - vol.Free(); - if st <> ds then st.Free(); - vi.Free(); - raise; - end; - - vi.fFactory := fac; - vi.fVolume := vol; - vi.fPackName := vfn; - vi.fStream := st; - vi.fPermanent := (permanent > 0); - vi.fNoDiskFile := (ds <> nil); - vi.fOpenedFilesCount := 0; -end; - -function SFSAddSubDataFile (const virtualName: AnsiString; ds: TStream; top: Boolean=false): Boolean; -var - tv: Integer; -begin - ASSERT(ds <> nil); - try - if top then tv := -1 else tv := 1; - SFSAddDataFileEx(virtualName, ds, tv, 0); - result := true; - except - result := false; - end; -end; - -function SFSAddDataFile (const dataFileName: AnsiString; top: Boolean=false): Boolean; -var - tv: Integer; -begin - try - if top then tv := -1 else tv := 1; - SFSAddDataFileEx(dataFileName, nil, tv, 1); - result := true; - except - result := false; - end; -end; - -function SFSAddDataFileTemp (const dataFileName: AnsiString; top: Boolean=false): Boolean; -var - tv: Integer; -begin - try - if top then tv := -1 else tv := 1; - SFSAddDataFileEx(dataFileName, nil, tv, 0); - result := true; - except - result := false; - end; -end; - - - -function SFSExpandDirName (const s: AnsiString): AnsiString; -var - f, e: Integer; - es: AnsiString; -begin - f := 1; result := s; - while f < Length(result) do - begin - while (f < Length(result)) and (result[f] <> '<') do Inc(f); - if f >= Length(result) then exit; - e := f; while (e < Length(result)) and (result[e] <> '>') do Inc(e); - es := Copy(result, f, e+1-f); - - if es = '' then es := GetCurrentDir - else if es = '' then es := ExtractFilePath(ParamStr(0)) - else es := ''; - - if es <> '' then - begin - if (es[Length(es)] <> '/') and (es[Length(es)] <> '\') then es := es+'/'; - Delete(result, f, e+1-f); - Insert(es, result, f); - Inc(f, Length(es)); - end - else f := e+1; - end; -end; - -function SFSFileOpenEx (const fName: AnsiString): TStream; -var - dataFileName, fn: AnsiString; - f: Integer; - vi: TVolumeInfo; - diskChecked: Boolean; - ps: TStream; - - function CheckDisk (): TStream; - // ïðîâåðèì, åñòü ëè ôàëî fn ãäå-òî íà äèñêàõ. - var - dfn, dirs, cdir: AnsiString; - f: Integer; - begin - result := nil; - if diskChecked or not sfsDiskEnabled then exit; - diskChecked := true; - dfn := SFSReplacePathDelims(fn, '/'); - dirs := sfsDiskDirs; if dirs = '' then dirs := ''; - while dirs <> '' do - begin - f := 1; while (f <= Length(dirs)) and (dirs[f] <> '|') do Inc(f); - cdir := Copy(dirs, 1, f-1); Delete(dirs, 1, f); - if cdir = '' then continue; - cdir := SFSReplacePathDelims(SFSExpandDirName(cdir), '/'); - if cdir[Length(cdir)] <> '/' then cdir := cdir+'/'; - try - result := TFileStream.Create(cdir+dfn, fmOpenRead or {fmShareDenyWrite}fmShareDenyNone); - exit; - except - end; - end; - end; - -begin - SplitFName(fName, dataFileName, fn); - if fn = '' then raise ESFSError.Create('invalid file name: "'+fName+'"'); - - diskChecked := false; - - if dataFileName <> '' then - begin - // ïðåôèêñîâàíûé ôàéë - if sfsForceDiskForPrefixed then - begin - result := CheckDisk(); - if result <> nil then exit; - end; - - f := SFSAddDataFileEx(dataFileName, nil, 0, 0); - vi := TVolumeInfo(volumes[f]); - - try - result := vi.fVolume.OpenFileEx(fn); - ps := TOwnedPartialStream.Create(vi, result, 0, result.Size, true); - except - result.Free(); - if (gcdisabled = 0) and not vi.fPermanent and (vi.fOpenedFilesCount < 1) then volumes[f] := nil; - result := CheckDisk(); // îáëîì ñ datafile, ïðîâåðèì äèñê - if result = nil then raise ESFSError.Create('file not found: "'+fName+'"'); - exit; - end; - //Inc(vi.fOpenedFilesCount); - result := ps; - exit; - end; - - // íåïðåôèêñîâàíûé ôàéë - if sfsDiskFirst then - begin - result := CheckDisk(); - if result <> nil then exit; - end; - // èùåì ïî âñåì ïåðìàíåíòíûì ïðåôèêñàì - f := 0; - while f < volumes.Count do - begin - vi := TVolumeInfo(volumes[f]); - if (vi <> nil) and vi.fPermanent then - begin - if vi.fVolume <> nil then - begin - result := vi.fVolume.OpenFileEx(fn); - if result <> nil then - begin - try - ps := TOwnedPartialStream.Create(vi, result, 0, result.Size, true); - result := ps; - //Inc(vi.fOpenedFilesCount); - except - FreeAndNil(result); - end; - end; - if result <> nil then exit; - end; - end; - Inc(f); - end; - result := CheckDisk(); - if result = nil then raise ESFSError.Create('file not found: "'+fName+'"'); -end; - -function SFSFileOpen (const fName: AnsiString): TStream; -begin - try - result := SFSFileOpenEx(fName); - except - result := nil; - end; -end; - -function SFSFileList (const dataFileName: AnsiString): TSFSFileList; -var - f: Integer; - vi: TVolumeInfo; -begin - result := nil; - if dataFileName = '' then exit; - - try - f := SFSAddDataFileEx(dataFileName, nil, 0, 0); - except - exit; - end; - vi := TVolumeInfo(volumes[f]); - - try - result := TSFSFileList.Create(vi.fVolume); - except - if (gcdisabled = 0) and not vi.fPermanent and (vi.fOpenedFilesCount < 1) then volumes[f] := nil; - end; -end; - - -initialization - factories := TObjectList.Create(true); - volumes := TObjectList.Create(true); -//finalization - //volumes.Free(); // it fails for some reason... Runtime 217 (^C hit). wtf?! - //factories.Free(); // not need to be done actually... -end. diff --git a/src/sfs/sfsPlainFS.pas b/src/sfs/sfsPlainFS.pas deleted file mode 100644 index 51b0c0d..0000000 --- a/src/sfs/sfsPlainFS.pas +++ /dev/null @@ -1,146 +0,0 @@ -(* Copyright (C) Doom 2D: Forever Developers - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3 of the License ONLY. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - *) -// simple grouping files w/o packing: -// Quake I/II .PAK (PACK) -// SiN .SIN (SPAK) -// -{$INCLUDE ../shared/a_modes.inc} -{$SCOPEDENUMS OFF} -{.$R+} -unit sfsPlainFS; - -interface - -uses - SysUtils, Classes, Contnrs, sfs; - - -type - TSFSPlainVolumeType = (sfspvNone, sfspvPAK, sfspvSIN); - - TSFSPlainVolume = class (TSFSVolume) - protected - fType: TSFSPlainVolumeType; - - procedure ReadDirectory (); override; - - public - function OpenFileByIndex (const index: Integer): TStream; override; - end; - - TSFSPlainVolumeFactory = class (TSFSVolumeFactory) - public - function IsMyVolumePrefix (const prefix: AnsiString): Boolean; override; - function Produce (const prefix, fileName: AnsiString; st: TStream): TSFSVolume; override; - procedure Recycle (vol: TSFSVolume); override; - end; - - -implementation - -uses - xstreams, utils; - - -{ TSFSPlainVolume } -procedure TSFSPlainVolume.ReadDirectory (); -var - dsize, dofs, esz: LongWord; - fi: TSFSFileInfo; - name: packed array [0..120] of Char; -begin - if (fType <> sfspvPAK) and (fType <> sfspvSIN) then raise ESFSError.Create('invalid archive'); - fFileStream.Seek(4, soCurrent); // skip signature - dofs := readLongWord(fFileStream); - dsize := readLongWord(fFileStream); - fFileStream.Position := dofs; - if fType = sfspvPAK then esz := 64 else esz := 128; - while dsize >= esz do - begin - fi := TSFSFileInfo.Create(self); - FillChar(name[0], length(name), 0); - fFileStream.ReadBuffer(name[0], esz-8); - fi.fName := PChar(@name[0]); - fi.fOfs := readLongWord(fFileStream); - fi.fSize := readLongWord(fFileStream); - Dec(dsize, esz); - end; -end; - -function TSFSPlainVolume.OpenFileByIndex (const index: Integer): TStream; -begin - result := nil; - if fFiles = nil then exit; - if (index < 0) or (index >= fFiles.Count) or (fFiles[index] = nil) then exit; - result := TSFSPartialStream.Create(fFileStream, TSFSFileInfo(fFiles[index]).fOfs, TSFSFileInfo(fFiles[index]).fSize, false); -end; - - -{ TSFSPlainVolumeFactory } -function TSFSPlainVolumeFactory.IsMyVolumePrefix (const prefix: AnsiString): Boolean; -begin - result := - StrEquCI1251(prefix, 'pak') or - StrEquCI1251(prefix, 'sin'); -end; - -procedure TSFSPlainVolumeFactory.Recycle (vol: TSFSVolume); -begin - vol.Free(); -end; - -function TSFSPlainVolumeFactory.Produce (const prefix, fileName: AnsiString; st: TStream): TSFSVolume; -var - vt: TSFSPlainVolumeType; - sign: packed array [0..3] of Char; - dsize, dofs: Integer; -begin - result := nil; - vt := sfspvNone; - - st.ReadBuffer(sign[0], 4); - dofs := readLongWord(st); - dsize := readLongWord(st); - st.Seek(-12, soCurrent); - if sign = 'PACK' then - begin - if (dsize < 0) or (dofs < 0) or (dofs > st.Size) or (dofs+dsize > st.Size) or (dsize mod 64 <> 0) then exit; - vt := sfspvPAK; - end - else if sign = 'SPAK' then - begin - if (dsize < 0) or (dofs < 0) or (dofs > st.Size) or (dofs+dsize > st.Size) or (dsize mod 64 <> 0) then exit; - vt := sfspvSIN; - end; - - result := TSFSPlainVolume.Create(fileName, st); - TSFSPlainVolume(result).fType := vt; - try - result.DoDirectoryRead(); - except - FreeAndNil(result); - raise; - end; -end; - - -var - pakf: TSFSPlainVolumeFactory; -initialization - pakf := TSFSPlainVolumeFactory.Create(); - SFSRegisterVolumeFactory(pakf); -//finalization -// SFSUnregisterVolumeFactory(pakf); -end. diff --git a/src/sfs/sfsZipFS.pas b/src/sfs/sfsZipFS.pas deleted file mode 100644 index 2f4c613..0000000 --- a/src/sfs/sfsZipFS.pas +++ /dev/null @@ -1,465 +0,0 @@ -(* Copyright (C) Doom 2D: Forever Developers - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3 of the License ONLY. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - *) -// grouping files with packing: -// zip, pk3: PKZIP-compatible archives (store, deflate) -// dfwad : D2D:F wad archives -// -{.$DEFINE SFS_DEBUG_ZIPFS} -{$INCLUDE ../shared/a_modes.inc} -{$SCOPEDENUMS OFF} -{.$R+} -unit sfsZipFS; - -interface - -uses - SysUtils, Classes, Contnrs, sfs; - - -type - TSFSZipVolumeType = (sfszvNone, sfszvZIP, sfszvDFWAD); - - TSFSZipVolume = class(TSFSVolume) - protected - fType: TSFSZipVolumeType; - - procedure ZIPReadDirectory (); - procedure DFWADReadDirectory (); - - procedure ReadDirectory (); override; - - public - function OpenFileByIndex (const index: Integer): TStream; override; - end; - - TSFSZipVolumeFactory = class(TSFSVolumeFactory) - public - function IsMyVolumePrefix (const prefix: AnsiString): Boolean; override; - function Produce (const prefix, fileName: AnsiString; st: TStream): TSFSVolume; override; - procedure Recycle (vol: TSFSVolume); override; - end; - - -implementation - -uses - xstreams, utils; - - -type - TSFSZipFileInfo = class(TSFSFileInfo) - public - fMethod: Byte; // 0: store; 8: deflate; 255: other - fPackSz: Int64; // can be -1 - end; - - TZLocalFileHeader = packed record - version: Byte; - hostOS: Byte; - flags: Word; - method: Word; - time: LongWord; - crc: LongWord; - packSz: LongWord; - unpackSz: LongWord; - fnameSz: Word; - localExtraSz: Word; - end; - -procedure readLFH (st: TStream; var hdr: TZLocalFileHeader); -{.$IFDEF ENDIAN_LITTLE} -begin - hdr.version := readByte(st); - hdr.hostOS := readByte(st); - hdr.flags := readWord(st); - hdr.method := readWord(st); - hdr.time := readLongWord(st); - hdr.crc := readLongWord(st); - hdr.packSz := readLongWord(st); - hdr.unpackSz := readLongWord(st); - hdr.fnameSz := readWord(st); - hdr.localExtraSz := readWord(st); -end; - - -function ZIPCheckMagic (st: TStream): Boolean; -var - sign: packed array [0..3] of Char; -begin - result := false; - st.ReadBuffer(sign[0], 4); - st.Seek(-4, soCurrent); - if (sign <> 'PK'#3#4) and (sign <> 'PK'#5#6) then exit; - result := true; -end; - - -function DFWADCheckMagic (st: TStream): Boolean; -var - sign: packed array [0..5] of Char; -begin - result := false; - if st.Size < 10 then exit; - st.ReadBuffer(sign[0], 6); - {fcnt :=} readWord(st); - st.Seek(-8, soCurrent); - if (sign[0] <> 'D') and (sign[1] <> 'F') and (sign[2] <> 'W') and - (sign[3] <> 'A') and (sign[4] <> 'D') and (sign[5] <> #$01) then exit; - result := true; -end; - - -{ TSFSZipVolume } -procedure TSFSZipVolume.ZIPReadDirectory (); -var - fi: TSFSZipFileInfo; - fname: AnsiString = ''; - sign: packed array [0..3] of Char; - lhdr: TZLocalFileHeader; - ignoreFile: Boolean; - efid, efsz: Word; - izver: Byte; - izcrc: LongWord; - buf: PByte; - bufsz, f: Integer; - cdofs, hdrofs: Int64; - cdsize: LongWord; - fileOffsets: array of Int64 = nil; - nameLen, extraLen, commentLen: Word; - fileIdx: Integer = -1; -begin - // search for central dir pointer - if fFileStream.size > 65636 then bufsz := 65636 else bufsz := fFileStream.size; - fFileStream.position := fFileStream.size-bufsz; - GetMem(buf, bufsz); - cdofs := -1; - cdsize := 0; - try - fFileStream.readBuffer(buf^, bufsz); - for f := bufsz-16 downto 4 do - begin - if (buf[f-4] = ord('P')) and (buf[f-3] = ord('K')) and (buf[f-2] = 5) and (buf[f-1] = 6) then - begin - cdsize := LongWord(buf[f+8])+(LongWord(buf[f+9])<<8)+(LongWord(buf[f+10])<<16)+(LongWord(buf[f+11])<<24); - cdofs := Int64(buf[f+12])+(Int64(buf[f+13])<<8)+(Int64(buf[f+14])<<16)+(Int64(buf[f+15])<<24); - break; - end; - end; - finally - FreeMem(buf); - end; - - if (cdofs >= 0) and (cdsize > 0) then - begin - // wow, we got central directory! process it - fFileStream.position := cdofs; - while cdsize >= 4 do - begin - Dec(cdsize, 4); - fFileStream.readBuffer(sign, 4); - if sign = 'PK'#1#2 then - begin - if cdsize < 42 then break; - Dec(cdsize, 42); - // skip uninteresting fields - fFileStream.seek(2+2+2+2+2+2+4+4+4, soCurrent); - nameLen := readWord(fFileStream); - extraLen := readWord(fFileStream); - commentLen := readWord(fFileStream); - // skip uninteresting fields - fFileStream.seek(2+2+4, soCurrent); - hdrofs := readLongWord(fFileStream); - // now skip name, extra and comment - if cdsize < nameLen+extraLen+commentLen then break; - Dec(cdsize, nameLen+extraLen+commentLen); - fFileStream.seek(nameLen+extraLen+commentLen, soCurrent); - SetLength(fileOffsets, length(fileOffsets)+1); - fileOffsets[high(fileOffsets)] := hdrofs; - //writeln('file #', high(fileOffsets), ' found at ', hdrofs); - end - else if sign = 'PK'#7#8 then - begin - if cdsize < 3*4 then break; - Dec(cdsize, 3*4); - fFileStream.seek(3*4, soCurrent); - end - else - begin - break; - end; - end; - if length(fileOffsets) = 0 then exit; // no files at all - fileIdx := 0; - end - else - begin - fFileStream.position := 0; - end; - - // read local directory - repeat - if fileIdx >= 0 then - begin - if fileIdx > High(fileOffsets) then break; - //writeln('reading file #', fileIdx, ' at ', fileOffsets[fileIdx]); - fFileStream.position := fileOffsets[fileIdx]; - Inc(fileIdx); - end; - - while true do - begin - fFileStream.ReadBuffer(sign[0], Length(sign)); - // skip data descriptor - if sign = 'PK'#7#8 then - begin - fFileStream.seek(3*4, soCurrent); - continue; - end; - break; - end; - if sign <> 'PK'#3#4 then break; - - ignoreFile := false; - - readLFH(fFileStream, lhdr); - - fi := TSFSZipFileInfo.Create(self); - fi.fPackSz := 0; - fi.fMethod := 0; - - SetLength(fname, lhdr.fnameSz); - if lhdr.fnameSz > 0 then - begin - fFileStream.ReadBuffer(fname[1], length(fname)); - fi.fName := utf8to1251(fname); - end; - - // here we should process extra field: it may contain utf8 filename - while lhdr.localExtraSz >= 4 do - begin - efid := readWord(fFileStream); - efsz := readWord(fFileStream); - Dec(lhdr.localExtraSz, 4); - if efsz > lhdr.localExtraSz then break; - // Info-ZIP Unicode Path Extra Field? - if (efid = $7075) and (efsz > 5) then - begin - fFileStream.ReadBuffer(izver, 1); - Dec(efsz, 1); - Dec(lhdr.localExtraSz, 1); - if izver = 1 then - begin - //writeln('!!!!!!!!!!!!'); - Dec(lhdr.localExtraSz, efsz); - fFileStream.ReadBuffer(izcrc, 4); // name crc, ignore it for now - Dec(efsz, 4); - SetLength(fname, efsz); - if length(fname) > 0 then fFileStream.readBuffer(fname[1], length(fname)); - fi.fName := utf8to1251(fname); - //writeln('++++++ [', fi.fName, ']'); - efsz := 0; - end; - end; - // skip it - if efsz > 0 then - begin - fFileStream.Seek(efsz, soCurrent); - Dec(lhdr.localExtraSz, efsz); - end; - end; - // skip the rest - if lhdr.localExtraSz > 0 then fFileStream.Seek(lhdr.localExtraSz, soCurrent); - - if (lhdr.flags and 1) <> 0 then - begin - // encrypted file: skip it - ignoreFile := true; - end; - - if (lhdr.method <> 0) and (lhdr.method <> 8) then - begin - // not stored. not deflated. skip. - ignoreFile := true; - end; - - if (length(fi.fName) = 0) or (fname[length(fi.fName)] = '/') or (fname[length(fi.fName)] = '\') then - begin - ignoreFile := true; - end - else - begin - for f := 1 to length(fi.fName) do if fi.fName[f] = '\' then fi.fName[f] := '/'; - end; - - fi.fOfs := fFileStream.Position; - fi.fSize := lhdr.unpackSz; - fi.fPackSz := lhdr.packSz; - fi.fMethod := lhdr.method; - if fi.fMethod = 0 then fi.fPackSz := fi.fSize; - - // skip packed data - if fileIdx < 0 then fFileStream.Seek(lhdr.packSz, soCurrent); - if ignoreFile then fi.Free(); - until false; - (* - if (sign <> 'PK'#1#2) and (sign <> 'PK'#5#6) then - begin - {$IFDEF SFS_DEBUG_ZIPFS} - WriteLn(ErrOutput, 'end: $', IntToHex(fFileStream.Position, 8)); - WriteLn(ErrOutput, 'sign: $', sign[0], sign[1], '#', ord(sign[2]), '#', ord(sign[3])); - {$ENDIF} - raise ESFSError.Create('invalid .ZIP archive (no central dir)'); - end; - *) -end; - - -procedure TSFSZipVolume.DFWADReadDirectory (); -// idiotic format -var - fcnt: Word; - fi: TSFSZipFileInfo; - f, c: Integer; - fofs, fpksize: LongWord; - curpath, fname: string; - name: packed array [0..15] of Char; -begin - curpath := ''; - fFileStream.Seek(6, soCurrent); // skip signature - fcnt := readWord(fFileStream); - if fcnt = 0 then exit; - // read files - for f := 0 to fcnt-1 do - begin - fFileStream.ReadBuffer(name[0], 16); - fofs := readLongWord(fFileStream); - fpksize := readLongWord(fFileStream); - c := 0; - fname := ''; - while (c < 16) and (name[c] <> #0) do - begin - if name[c] = '\' then name[c] := '/' - else if name[c] = '/' then name[c] := '_'; - fname := fname+name[c]; - Inc(c); - end; - // new directory? - if (fofs = 0) and (fpksize = 0) then - begin - if length(fname) <> 0 then fname := fname+'/'; - curpath := fname; - continue; - end; - if length(fname) = 0 then continue; // just in case - //writeln('DFWAD: [', curpath, '] [', fname, '] at ', fofs, ', size ', fpksize); - // create file record - fi := TSFSZipFileInfo.Create(self); - fi.fOfs := fofs; - fi.fSize := -1; - fi.fPackSz := fpksize; - fi.fName := fname; - fi.fPath := curpath; - fi.fMethod := 255; - end; -end; - -procedure TSFSZipVolume.ReadDirectory (); -begin - case fType of - sfszvZIP: ZIPReadDirectory(); - sfszvDFWAD: DFWADReadDirectory(); - else raise ESFSError.Create('invalid archive'); - end; -end; - -function TSFSZipVolume.OpenFileByIndex (const index: Integer): TStream; -var - rs: TStream; -begin - result := nil; - rs := nil; - if fFiles = nil then exit; - if (index < 0) or (index >= fFiles.Count) or (fFiles[index] = nil) then exit; - try - if TSFSZipFileInfo(fFiles[index]).fMethod = 0 then - begin - result := TSFSPartialStream.Create(fFileStream, TSFSZipFileInfo(fFiles[index]).fOfs, TSFSZipFileInfo(fFiles[index]).fSize, false); - end - else - begin - rs := TSFSPartialStream.Create(fFileStream, TSFSZipFileInfo(fFiles[index]).fOfs, TSFSZipFileInfo(fFiles[index]).fPackSz, false); - result := TUnZStream.Create(rs, TSFSZipFileInfo(fFiles[index]).fSize, true, (TSFSZipFileInfo(fFiles[index]).fMethod <> 255)); - end; - except - FreeAndNil(rs); - result := nil; - exit; - end; -end; - - -{ TSFSZipVolumeFactory } -function TSFSZipVolumeFactory.IsMyVolumePrefix (const prefix: AnsiString): Boolean; -begin - result := - StrEquCI1251(prefix, 'zip') or - StrEquCI1251(prefix, 'pk3') or - StrEquCI1251(prefix, 'dfz') or - StrEquCI1251(prefix, 'dfwad') or - StrEquCI1251(prefix, 'dfzip'); -end; - -procedure TSFSZipVolumeFactory.Recycle (vol: TSFSVolume); -begin - vol.Free(); -end; - -function TSFSZipVolumeFactory.Produce (const prefix, fileName: AnsiString; st: TStream): TSFSVolume; -var - vt: TSFSZipVolumeType; -begin - vt := sfszvNone; - if ZIPCheckMagic(st) then vt := sfszvZIP - else if DFWADCheckMagic(st) then vt := sfszvDFWAD; - - if vt <> sfszvNone then - begin - result := TSFSZipVolume.Create(fileName, st); - TSFSZipVolume(result).fType := vt; - try - result.DoDirectoryRead(); - except {$IFDEF SFS_DEBUG_ZIPFS} on e: Exception do begin - WriteLn(errOutput, 'ZIP ERROR: [', e.ClassName, ']: ', e.Message); - {$ENDIF} - FreeAndNil(result); - raise; - {$IFDEF SFS_DEBUG_ZIPFS}end;{$ENDIF} - end; - end - else - begin - result := nil; - end; -end; - - -var - zipf: TSFSZipVolumeFactory; -initialization - zipf := TSFSZipVolumeFactory.Create(); - SFSRegisterVolumeFactory(zipf); -//finalization -// SFSUnregisterVolumeFactory(zipf); -end. diff --git a/src/shared/dfzip.pas b/src/shared/dfzip.pas deleted file mode 100644 index 0d2ac8a..0000000 --- a/src/shared/dfzip.pas +++ /dev/null @@ -1,390 +0,0 @@ -(* Copyright (C) Doom 2D: Forever Developers - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3 of the License ONLY. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - *) -{$INCLUDE ../shared/a_modes.inc} -unit dfzip; - - (** Based on WadCvt tool **) - -interface - - uses SysUtils, Classes; - - type - TFileInfo = class - public - name: AnsiString; - pkofs: Int64; // offset of file header - size: Int64; - pksize: Int64; - crc: LongWord; - method: Word; - - constructor Create (); - end; - - function ZipOne (ds: TStream; fname: AnsiString; st: TStream; dopack: Boolean=true): TFileInfo; - procedure writeCentralDir (ds: TStream; files: array of TFileInfo); - -implementation - - uses utils, xstreams, crc, paszlib, e_log; - - const - uni2wint: array [128..255] of Word = ( - $0402,$0403,$201A,$0453,$201E,$2026,$2020,$2021,$20AC,$2030,$0409,$2039,$040A,$040C,$040B,$040F, - $0452,$2018,$2019,$201C,$201D,$2022,$2013,$2014,$003F,$2122,$0459,$203A,$045A,$045C,$045B,$045F, - $00A0,$040E,$045E,$0408,$00A4,$0490,$00A6,$00A7,$0401,$00A9,$0404,$00AB,$00AC,$00AD,$00AE,$0407, - $00B0,$00B1,$0406,$0456,$0491,$00B5,$00B6,$00B7,$0451,$2116,$0454,$00BB,$0458,$0405,$0455,$0457, - $0410,$0411,$0412,$0413,$0414,$0415,$0416,$0417,$0418,$0419,$041A,$041B,$041C,$041D,$041E,$041F, - $0420,$0421,$0422,$0423,$0424,$0425,$0426,$0427,$0428,$0429,$042A,$042B,$042C,$042D,$042E,$042F, - $0430,$0431,$0432,$0433,$0434,$0435,$0436,$0437,$0438,$0439,$043A,$043B,$043C,$043D,$043E,$043F, - $0440,$0441,$0442,$0443,$0444,$0445,$0446,$0447,$0448,$0449,$044A,$044B,$044C,$044D,$044E,$044F - ); - -constructor TFileInfo.Create; -begin - name := ''; - pkofs := 0; - size := 0; - pksize := 0; - crc := crc32(0, nil, 0); - method := 0; -end; - -function toUtf8 (const s: AnsiString): AnsiString; -var - uc: PUnicodeChar; - xdc: PChar; - pos, f: Integer; -begin - GetMem(uc, length(s)*8); - GetMem(xdc, length(s)*8); - try - FillChar(uc^, length(s)*8, 0); - FillChar(xdc^, length(s)*8, 0); - pos := 0; - for f := 1 to length(s) do - begin - if ord(s[f]) < 128 then - uc[pos] := UnicodeChar(ord(s[f])) - else - uc[pos] := UnicodeChar(uni2wint[ord(s[f])]); - Inc(pos); - end; - FillChar(xdc^, length(s)*8, 0); - f := UnicodeToUtf8(xdc, length(s)*8, uc, pos); - while (f > 0) and (xdc[f-1] = #0) do Dec(f); - SetLength(result, f); - Move(xdc^, result[1], f); - finally - FreeMem(xdc); - FreeMem(uc); - end; -end; - -// returs crc -function zpack (ds: TStream; ss: TStream; var aborted: Boolean): LongWord; -const - IBSize = 65536; - OBSize = 65536; -var - zst: TZStream; - ib, ob: PByte; - err: Integer; - rd: Integer; - eof: Boolean; - crc: LongWord; - dstp, srcsize: Int64; -begin - result := 0; - //aborted := true; exit; - aborted := false; - crc := crc32(0, nil, 0); - GetMem(ib, IBSize); - GetMem(ob, OBSize); - ss.position := 0; - dstp := ds.position; - srcsize := ss.size; - try - zst.next_out := ob; - zst.avail_out := OBSize; - zst.next_in := ib; - zst.avail_in := 0; - err := deflateInit2(zst, Z_BEST_COMPRESSION, Z_DEFLATED, -15, 9, 0); - if err <> Z_OK then raise Exception.Create(zerror(err)); - try - eof := false; - repeat - if zst.avail_in = 0 then - begin - // read input buffer part - rd := ss.read(ib^, IBSize); - if rd < 0 then raise Exception.Create('reading error'); - //writeln(' read ', rd, ' bytes'); - eof := (rd = 0); - if rd <> 0 then begin crc := crc32(crc, Pointer(ib), rd); result := crc; end; - zst.next_in := ib; - zst.avail_in := rd; - end; - // now process the whole input - while zst.avail_in > 0 do - begin - err := deflate(zst, Z_NO_FLUSH); - if err <> Z_OK then raise Exception.Create(zerror(err)); - if zst.avail_out < OBSize then - begin - //writeln(' written ', OBSize-zst.avail_out, ' bytes'); - if ds.position+(OBSize-zst.avail_out)-dstp >= srcsize then - begin - // this will be overwritten anyway - aborted := true; - exit; - end; - ds.writeBuffer(ob^, OBSize-zst.avail_out); - zst.next_out := ob; - zst.avail_out := OBSize; - end; - end; - until eof; - // do leftovers - while true do - begin - zst.avail_in := 0; - err := deflate(zst, Z_FINISH); - if (err <> Z_OK) and (err <> Z_STREAM_END) then raise Exception.Create(zerror(err)); - if zst.avail_out < OBSize then - begin - //writeln(' .written ', OBSize-zst.avail_out, ' bytes'); - if ds.position+(OBSize-zst.avail_out)-dstp >= srcsize then - begin - // this will be overwritten anyway - aborted := true; - exit; - end; - ds.writeBuffer(ob^, OBSize-zst.avail_out); - zst.next_out := ob; - zst.avail_out := OBSize; - end; - if err <> Z_OK then break; - end; - // succesfully flushed? - if (err <> Z_STREAM_END) then raise Exception.Create(zerror(err)); - finally - deflateEnd(zst); - end; - finally - FreeMem(ob); - FreeMem(ib); - end; -end; - -// this will write "extra field length" and extra field itself -{$IFDEF UTFEXTRA} -const UtfFlags = 0; - -type - TByteArray = array of Byte; - -function buildUtfExtra (fname: AnsiString): TByteArray; -var - crc: LongWord; - fu: AnsiString; - sz: Word; -begin - fu := toUtf8(fname); - if fu = fname then begin result := nil; exit; end; // no need to write anything - crc := crc32(0, @fname[1], length(fname)); - sz := 2+2+1+4+length(fu); - SetLength(result, sz); - result[0] := ord('u'); - result[1] := ord('p'); - Dec(sz, 4); - result[2] := sz and $ff; - result[3] := (sz shr 8) and $ff; - result[4] := 1; - result[5] := crc and $ff; - result[6] := (crc shr 8) and $ff; - result[7] := (crc shr 16) and $ff; - result[8] := (crc shr 24) and $ff; - Move(fu[1], result[9], length(fu)); -end; -{$ELSE} -const UtfFlags = (1 shl 10); // bit 11 -{$ENDIF} - -function ZipOne (ds: TStream; fname: AnsiString; st: TStream; dopack: Boolean=true): TFileInfo; -var - oldofs, nfoofs, pkdpos, rd: Int64; - sign: packed array [0..3] of Char; - buf: PChar; - bufsz: Integer; - aborted: Boolean = false; -{$IFDEF UTFEXTRA} - ef: TByteArray; -{$ENDIF} -begin - result := TFileInfo.Create(); - result.pkofs := ds.position; - result.size := st.size; - if result.size > 0 then result.method := 8 else result.method := 0; - if not dopack then - begin - result.method := 0; - result.pksize := result.size; - end; -{$IFDEF UTFEXTRA} - result.name := fname; - ef := buildUtfExtra(result.name); -{$ELSE} - result.name := toUtf8(fname); -{$ENDIF} - // write local header - sign := 'PK'#3#4; - ds.writeBuffer(sign, 4); - writeInt(ds, Word($0A10)); // version to extract - writeInt(ds, Word(UtfFlags)); // flags - writeInt(ds, Word(result.method)); // compression method - writeInt(ds, Word(0)); // file time - writeInt(ds, Word(0)); // file date - nfoofs := ds.position; - writeInt(ds, LongWord(result.crc)); // crc32 - writeInt(ds, LongWord(result.pksize)); // packed size - writeInt(ds, LongWord(result.size)); // unpacked size - writeInt(ds, Word(length(fname))); // name length -{$IFDEF UTFEXTRA} - writeInt(ds, Word(length(ef))); // extra field length -{$ELSE} - writeInt(ds, Word(0)); // extra field length -{$ENDIF} - ds.writeBuffer(fname[1], length(fname)); -{$IFDEF UTFEXTRA} - if length(ef) > 0 then ds.writeBuffer(ef[0], length(ef)); -{$ENDIF} - if dopack then - begin - // now write packed data - if result.size > 0 then - begin - pkdpos := ds.position; - st.position := 0; - result.crc := zpack(ds, st, aborted); - result.pksize := ds.position-pkdpos; - if {result.pksize >= result.size} aborted then - begin - // there's no sence to pack this file, so just store it - st.position := 0; - ds.position := result.pkofs; - result.Free(); - // store it - result := ZipOne(ds, fname, st, false); - exit; - end - else - begin - // fix header - oldofs := ds.position; - ds.position := nfoofs; - writeInt(ds, LongWord(result.crc)); // crc32 - writeInt(ds, LongWord(result.pksize)); // crc32 - ds.position := oldofs; - end; - end; - end - else - begin - bufsz := 1024*1024; - GetMem(buf, bufsz); - try - st.position := 0; - result.crc := crc32(0, nil, 0); - result.pksize := 0; - while result.pksize < result.size do - begin - rd := result.size-result.pksize; - if rd > bufsz then rd := bufsz; - st.readBuffer(buf^, rd); - ds.writeBuffer(buf^, rd); - Inc(result.pksize, rd); - result.crc := crc32(result.crc, buf, rd); - end; - finally - FreeMem(buf); - end; - // fix header - oldofs := ds.position; - ds.position := nfoofs; - writeInt(ds, LongWord(result.crc)); // crc32 - ds.position := oldofs; - //write('(S) '); - end; -end; - - -procedure writeCentralDir (ds: TStream; files: array of TFileInfo); -var - cdofs, cdend: Int64; - sign: packed array [0..3] of Char; - f: Integer; -{$IFDEF UTFEXTRA} - ef: TByteArray; -{$ENDIF} -begin - cdofs := ds.position; - for f := 0 to high(files) do - begin -{$IFDEF UTFEXTRA} - ef := buildUtfExtra(files[f].name); -{$ENDIF} - sign := 'PK'#1#2; - ds.writeBuffer(sign, 4); - writeInt(ds, Word($0A10)); // version made by - writeInt(ds, Word($0010)); // version to extract - writeInt(ds, Word(UtfFlags)); // flags - writeInt(ds, Word(files[f].method)); // compression method - writeInt(ds, Word(0)); // file time - writeInt(ds, Word(0)); // file date - writeInt(ds, LongWord(files[f].crc)); - writeInt(ds, LongWord(files[f].pksize)); - writeInt(ds, LongWord(files[f].size)); - writeInt(ds, Word(length(files[f].name))); // name length -{$IFDEF UTFEXTRA} - writeInt(ds, Word(length(ef))); // extra field length -{$ELSE} - writeInt(ds, Word(0)); // extra field length -{$ENDIF} - writeInt(ds, Word(0)); // comment length - writeInt(ds, Word(0)); // disk start - writeInt(ds, Word(0)); // internal attributes - writeInt(ds, LongWord(0)); // external attributes - writeInt(ds, LongWord(files[f].pkofs)); // header offset - ds.writeBuffer(files[f].name[1], length(files[f].name)); -{$IFDEF UTFEXTRA} - if length(ef) > 0 then ds.writeBuffer(ef[0], length(ef)); -{$ENDIF} - end; - cdend := ds.position; - // write end of central dir - sign := 'PK'#5#6; - ds.writeBuffer(sign, 4); - writeInt(ds, Word(0)); // disk number - writeInt(ds, Word(0)); // disk with central dir - writeInt(ds, Word(length(files))); // number of files on this dist - writeInt(ds, Word(length(files))); // number of files total - writeInt(ds, LongWord(cdend-cdofs)); // size of central directory - writeInt(ds, LongWord(cdofs)); // central directory offset - writeInt(ds, Word(0)); // archive comment length -end; - -end. diff --git a/src/shared/xstreams.pas b/src/shared/xstreams.pas deleted file mode 100644 index 36e73f9..0000000 --- a/src/shared/xstreams.pas +++ /dev/null @@ -1,566 +0,0 @@ -(* Copyright (C) Doom 2D: Forever Developers - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3 of the License ONLY. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - *) -// special stream classes -{$INCLUDE a_modes.inc} -{.$R+} -unit xstreams; - -interface - -uses - SysUtils, Classes, - zbase{z_stream}; - - -type - XStreamError = class(Exception); - - // read-only ïîòîê äëÿ èçâëå÷åíèÿ èç èñõîäíîãî òîëüêî êóñî÷êà - TSFSPartialStream = class(TStream) - protected - fSource: TStream; // èñõîäíûé ïîòîê - fKillSource: Boolean; // óáèâàòü èñõîäíèê ïðè ïîìèðàíèè? - fLastReadPos: Int64; // ïîñëåäíèé Read() îñòàíîâèëñÿ çäåñü (îòíîñ. fStartPos) - fCurrentPos: Int64; // ïîñëåäíèé Seek() îñòàíîâèëñÿ çäåñü (îòíîñ. fStartPos) - fStartPos: Int64; // íà÷àëî êóñî÷êà - fSize: Int64; // äëèíà êóñî÷êà - fPreBuf: packed array of Byte; // ýòîò áóôåð áóäåò ïåðåä ôàéëîì - - procedure CheckPos (); - - public - // aSrc: ïîòîê-èñõîäíèê. - // aPos: íà÷àëüíàÿ ïîçèöèÿ â ïîòîêå. -1 -- ñ òåêóùåé. - // åñëè aPos < òåêóùåé ïîçèöèè, òî èñõîäíûé ïîòîê äîëæåí - // íîðìàëüíî ïîääåðæèâàòü Seek()! - // aSize: êîëè÷åñòâî áàéòèêîâ, êîòîðîå ìîæíî ïðî÷åñòü èç ïîòîêà. - // åñëè ìåíüøå íóëÿ -- òî äî êîíöà. - // aKillSrc: óáèâàòü ëè èñõîäíûé ïîòîê, êîãäà ñàìè óìèðàåì? - // òàêæå ìîæåò ïðèøïàíäîðèòü ê íà÷àëó ôàéëà áóôåð. bufSz áóäåò äîáàâëåíî ê - // äëèíå ôàéëà. - constructor Create (aSrc: TStream; aPos, aSize: Int64; aKillSrc: Boolean; preBuf: Pointer=nil; bufSz: Integer=0); - destructor Destroy (); override; - - // íîðìàëèçóåò count è ÷èòàåò. - function Read (var buffer; count: LongInt): LongInt; override; - // Write() ïðîñòî ãðîìêî ïàäàåò. - function Write (const buffer; count: LongInt): LongInt; override; - // Seek() ðåàëèçîâàíî, ÷òîáû ìîãëà ðàáîòàòü ïðîïåðòÿ Size. - // âîîáùå-òî ìîæíî ïåðåêðûòü ìåòîä GetSize(), íî âäðóã êàêîé - // áîëüíîé íà ãîëîâó êîäåð áóäåò ïîëó÷àòü ðàçìåð ïðè ïîìîùè - // Seek()'à? - function Seek (const offset: Int64; origin: TSeekOrigin): Int64; override; - end; - - // this stream can kill both `proxied` and `guarded` streams on closing - TSFSGuardStream = class(TStream) - protected - fSource: TStream; // èñõîäíûé ïîòîê - fGuardedStream: TStream; // ïîòîê, êîòîðûé çàâàëèì ïðè ïîìèðàíèè - fKillSource: Boolean; // óáèâàòü èñõîäíèê ïðè ïîìèðàíèè? - fKillGuarded: Boolean; // óáèâàòü îõðàíÿåìûé ïðè ïîìèðàíèè? - fGuardedFirst: Boolean; // ïðè ñìåðòè ïåðâûì ïðèøèáàåì îõðàíÿåìîãî? - - public - // aSrc: ïîòîê-èñõîäíèê (íà êîòîðûé çàìàïåíû îïåðàöèè ÷òåíèÿ/çàïèñè). - // aKillSrc: óáèâàòü ëè èñõîäíûé ïîòîê, êîãäà ñàìè óìèðàåì? - // aKillGuarded: óáèâàòü ëè îõðàíÿåìûé ïîòîê, êîãäà ñàìè óìèðàåì? - // aGuardedFirst: true: ïðè ñìåðòè ïåðâûì ïðèøèáàåì îõðàíÿåìîãî. - constructor Create (aSrc, aGuarded: TStream; aKillSrc, aKillGuarded: Boolean; aGuardedFirst: Boolean=true); - destructor Destroy (); override; - - // íèæåñëåäóþùåå çàìàïëåíî íà fSource - function Read (var buffer; count: LongInt): LongInt; override; - function Write (const buffer; count: LongInt): LongInt; override; - function Seek (const offset: Int64; origin: TSeekOrigin): Int64; override; - end; - - TSFSMemoryStreamRO = class(TCustomMemoryStream) - private - fFreeMem: Boolean; - fMem: Pointer; - - public - constructor Create (pMem: Pointer; pSize: Integer; aFreeMem: Boolean=false); - destructor Destroy (); override; - - function Write (const buffer; count: LongInt): LongInt; override; - end; - - TUnZStream = class(TStream) - protected - fSrcSt: TStream; - fZlibSt: z_stream; - fBuffer: PByte; - fPos: Int64; - fSkipHeader: Boolean; - fSize: Int64; // can be -1 - fSrcStPos: Int64; - fSkipToPos: Int64; // >0: skip to this position - fKillSrc: Boolean; - - procedure reset (); - function readBuf (var buffer; count: LongInt): LongInt; - procedure fixPos (); - procedure determineSize (); - - public - // `aSize` can be -1 if stream size is unknown - constructor create (asrc: TStream; aSize: Int64; aKillSrc: Boolean; aSkipHeader: boolean=false); - destructor destroy (); override; - function read (var buffer; count: LongInt): LongInt; override; - function write (const buffer; count: LongInt): LongInt; override; - function seek (const offset: Int64; origin: TSeekOrigin): Int64; override; - end; - - // fixed memory chunk - TSFSMemoryChunkStream = class(TStream) - private - fFreeMem: Boolean; - fMemBuf: PByte; - fMemSize: Integer; - fCurPos: Integer; - - public - // if `pMem` is `nil`, stream will allocate it - constructor Create (pMem: Pointer; pSize: Integer; aFreeMem: Boolean=false); - destructor Destroy (); override; - - procedure setup (pMem: Pointer; pSize: Integer; aFreeMem: Boolean=false); - - function Seek (const offset: Int64; origin: TSeekOrigin): Int64; override; - function Read (var buffer; count: LongInt): LongInt; override; - function Write (const buffer; count: LongInt): LongInt; override; - - property chunkSize: Integer read fMemSize; - property chunkData: PByte read fMemBuf; - end; - - -implementation - -uses - zinflate; - - -{ TSFSPartialStream } -constructor TSFSPartialStream.Create (aSrc: TStream; aPos, aSize: Int64; aKillSrc: Boolean; preBuf: Pointer=nil; bufSz: Integer=0); -begin - inherited Create(); - ASSERT(aSrc <> nil); - if aPos < 0 then aPos := aSrc.Position; - if aSize < 0 then aSize := 0; - fSource := aSrc; - fKillSource := aKillSrc; - fLastReadPos := 0; - fCurrentPos := 0; - fStartPos := aPos; - fSize := aSize; - if bufSz > 0 then - begin - SetLength(fPreBuf, bufSz); - Move(preBuf^, fPreBuf[0], bufSz); - Inc(fSize, bufSz); - end - else - begin - fPreBuf := nil; - end; -end; - -destructor TSFSPartialStream.Destroy (); -begin - if fKillSource then FreeAndNil(fSource); - inherited Destroy(); -end; - -procedure TSFSPartialStream.CheckPos (); -begin - { - if fSource.Position <> fStartPos+fCurrentPos-Length(fPreBuf) then - begin - fSource.Position := fStartPos+fCurrentPos-Length(fPreBuf); - end; - } - if fCurrentPos >= length(fPreBuf) then - begin - //writeln('seeking at ', fCurrentPos, ' (real: ', fStartPos+fCurrentPos-Length(fPreBuf), ')'); - fSource.Position := fStartPos+fCurrentPos-Length(fPreBuf); - end; - fLastReadPos := fCurrentPos; -end; - -function TSFSPartialStream.Write (const buffer; count: LongInt): LongInt; -begin - result := 0; - raise XStreamError.Create('can''t write to read-only stream'); - // à íå õîäè, íåõîðîøèé, â íàø ñàäèê ãóëÿòü! -end; - -function TSFSPartialStream.Read (var buffer; count: LongInt): LongInt; -var - left: Int64; - pc: Pointer; - rd: LongInt; -begin - if count < 0 then raise XStreamError.Create('invalid Read() call'); // ñêàçî÷íûé äîëáî¸á... - if count = 0 then begin result := 0; exit; end; - pc := @buffer; - result := 0; - if (Length(fPreBuf) > 0) and (fCurrentPos < Length(fPreBuf)) then - begin - fLastReadPos := fCurrentPos; - left := Length(fPreBuf)-fCurrentPos; - if left > count then left := count; - if left > 0 then - begin - Move(fPreBuf[fCurrentPos], pc^, left); - Inc(PChar(pc), left); - Inc(fCurrentPos, left); - fLastReadPos := fCurrentPos; - Dec(count, left); - result := left; - if count = 0 then exit; - end; - end; - CheckPos(); - left := fSize-fCurrentPos; - if left < count then count := left; // è òàê ñëó÷àåòñÿ... - if count > 0 then - begin - rd := fSource.Read(pc^, count); - Inc(result, rd); - Inc(fCurrentPos, rd); - fLastReadPos := fCurrentPos; - end - else - begin - result := 0; - end; -end; - -function TSFSPartialStream.Seek (const offset: Int64; origin: TSeekOrigin): Int64; -begin - case origin of - soBeginning: result := offset; - soCurrent: result := offset+fCurrentPos; - soEnd: result := fSize+offset; - else raise XStreamError.Create('invalid Seek() call'); - // äðóãèõ íå áûâàåò. à ó êîãî áûâàåò, òîìó ÿ íå äîêòîð. - end; - if result < 0 then result := 0 - else if result > fSize then result := fSize; - fCurrentPos := result; -end; - - -{ TSFSGuardStream } -constructor TSFSGuardStream.Create (aSrc, aGuarded: TStream; aKillSrc, aKillGuarded: Boolean; aGuardedFirst: Boolean=true); -begin - inherited Create(); - fSource := aSrc; fGuardedStream := aGuarded; - fKillSource := aKillSrc; fKillGuarded := aKillGuarded; - fGuardedFirst := aGuardedFirst; -end; - -destructor TSFSGuardStream.Destroy (); -begin - if fKillGuarded and fGuardedFirst then FreeAndNil(fGuardedStream); - if fKillSource then FreeAndNil(fSource); - if fKillGuarded and not fGuardedFirst then FreeAndNil(fGuardedStream); - inherited Destroy(); -end; - -function TSFSGuardStream.Read (var buffer; count: LongInt): LongInt; -begin - result := fSource.Read(buffer, count); -end; - -function TSFSGuardStream.Write (const buffer; count: LongInt): LongInt; -begin - result := fSource.Write(buffer, count); -end; - -function TSFSGuardStream.Seek (const offset: Int64; origin: TSeekOrigin): Int64; -begin - result := fSource.Seek(offset, origin); -end; - - -{ TSFSMemoryStreamRO } -constructor TSFSMemoryStreamRO.Create (pMem: Pointer; pSize: Integer; aFreeMem: Boolean=false); -begin - fFreeMem := aFreeMem; - fMem := pMem; - inherited Create(); - SetPointer(pMem, pSize); - Position := 0; -end; - -destructor TSFSMemoryStreamRO.Destroy (); -begin - if fFreeMem and (fMem <> nil) then FreeMem(fMem); -end; - -function TSFSMemoryStreamRO.Write (const buffer; count: LongInt): LongInt; -begin - result := 0; - raise XStreamError.Create('can''t write to read-only stream'); - // ñîâñåì ñáðåíäèë... -end; - - -// ////////////////////////////////////////////////////////////////////////// // -{ TUnZStream } -const ZBufSize = 32768; // size of the buffer used for temporarily storing data from the child stream - - -constructor TUnZStream.create (asrc: TStream; aSize: Int64; aKillSrc: Boolean; aSkipHeader: boolean=false); -var - err: Integer; -begin - fKillSrc := aKillSrc; - fPos := 0; - fSkipToPos := -1; - fSrcSt := asrc; - fSize := aSize; - GetMem(fBuffer, ZBufSize); - fSkipHeader := aSkipHeader; - fSrcStPos := fSrcSt.position; - FillChar(fZlibSt, sizeof(fZlibSt), 0); - if fSkipHeader then err := inflateInit2(fZlibSt, -MAX_WBITS) else err := inflateInit(fZlibSt); - if err <> Z_OK then raise XStreamError.Create(zerror(err)); -end; - - -destructor TUnZStream.destroy (); -begin - inflateEnd(fZlibSt); - FreeMem(fBuffer); - if fKillSrc then fSrcSt.Free(); - inherited Destroy(); -end; - - -function TUnZStream.readBuf (var buffer; count: LongInt): LongInt; -var - err: Integer; - sz: LongInt; -begin - result := 0; - if (fSize >= 0) and (fPos >= fSize) then exit; - if count > 0 then - begin - fZlibSt.next_out := @buffer; - fZlibSt.avail_out := count; - sz := fZlibSt.avail_out; - while fZlibSt.avail_out > 0 do - begin - if fZlibSt.avail_in = 0 then - begin - // refill the buffer - fZlibSt.next_in := fBuffer; - fZlibSt.avail_in := fSrcSt.read(Fbuffer^, ZBufSize); - end; - err := inflate(fZlibSt, Z_NO_FLUSH); - if (err <> Z_OK) and (err <> Z_STREAM_END) then raise XStreamError.Create(zerror(err)); - Inc(result, sz-fZlibSt.avail_out); - Inc(fPos, sz-fZlibSt.avail_out); - sz := fZlibSt.avail_out; - if err = Z_STREAM_END then begin fSize := fPos; break; end; - end; - end; -end; - - -procedure TUnZStream.fixPos (); -var - buf: array [0..4095] of Byte; - rd, rr: LongInt; -begin - if fSkipToPos < 0 then exit; - //writeln('fixing pos: fPos=', fPos, '; fSkipToPos=', fSkipToPos); - if fSkipToPos < fPos then reset(); - while fPos < fSkipToPos do - begin - if fSkipToPos-fPos > 4096 then rd := 4096 else rd := LongInt(fSkipToPos-fPos); - //writeln(' reading ', rd, ' bytes...'); - rr := readBuf(buf, rd); - //writeln(' got ', rr, ' bytes; fPos=', fPos, '; fSkipToPos=', fSkipToPos); - if rr <= 0 then raise XStreamError.Create('seek error'); - end; - //writeln(' pos: fPos=', fPos, '; fSkipToPos=', fSkipToPos); - fSkipToPos := -1; -end; - - -procedure TUnZStream.determineSize (); -var - buf: array [0..4095] of Byte; - rd: LongInt; - opos: Int64; -begin - if fSize >= 0 then exit; - opos := fPos; - try - //writeln('determining unzstream size...'); - while true do - begin - rd := readBuf(buf, 4096); - if rd = 0 then break; - end; - fSize := fPos; - //writeln(' unzstream size is ', fSize); - finally - if fSkipToPos < 0 then fSkipToPos := opos; - end; -end; - - -function TUnZStream.read (var buffer; count: LongInt): LongInt; -begin - if fSkipToPos >= 0 then fixPos(); - result := readBuf(buffer, count); -end; - - -function TUnZStream.write (const buffer; count: LongInt): LongInt; -begin - result := 0; - raise XStreamError.Create('can''t write to read-only stream'); -end; - - -procedure TUnZStream.reset (); -var - err: Integer; -begin - //writeln('doing RESET'); - fSrcSt.position := fSrcStPos; - fPos := 0; - inflateEnd(fZlibSt); - FillChar(fZlibSt, sizeof(fZlibSt), 0); - if fSkipHeader then err := inflateInit2(fZlibSt, -MAX_WBITS) else err := inflateInit(fZlibSt); - if err <> Z_OK then raise XStreamError.Create(zerror(err)); -end; - - -function TUnZStream.Seek (const offset: Int64; origin: TSeekOrigin): Int64; -var - cpos: Int64; -begin - cpos := fPos; - if fSkipToPos >= 0 then cpos := fSkipToPos; - case origin of - soBeginning: result := offset; - soCurrent: result := offset+cpos; - soEnd: begin determineSize(); result := fSize+offset; end; - else raise XStreamError.Create('invalid Seek() call'); - // äðóãèõ íå áûâàåò. à ó êîãî áûâàåò, òîìó ÿ íå äîêòîð. - end; - if result < 0 then result := 0; - fSkipToPos := result; - //writeln('seek: ofs=', offset, '; origin=', origin, '; result=', result); -end; - - -// ////////////////////////////////////////////////////////////////////////// // -constructor TSFSMemoryChunkStream.Create (pMem: Pointer; pSize: Integer; aFreeMem: Boolean=false); -begin - fMemBuf := nil; - fFreeMem := false; - fMemSize := 0; - fCurPos := 0; - setup(pMem, pSize, aFreeMem); -end; - - -procedure TSFSMemoryChunkStream.setup (pMem: Pointer; pSize: Integer; aFreeMem: Boolean=false); -begin - if fFreeMem then FreeMem(fMemBuf); - fMemBuf := nil; - fFreeMem := false; - fMemSize := 0; - fCurPos := 0; - if (pSize < 0) then raise XStreamError.Create('invalid chunk size'); - if (pMem = nil) then - begin - if (pSize > 0) then - begin - GetMem(pMem, pSize); - if (pMem = nil) then raise XStreamError.Create('out of memory for chunk'); - aFreeMem := true; - end - else - begin - aFreeMem := false; - end; - end; - fFreeMem := aFreeMem; - fMemBuf := PByte(pMem); - fMemSize := pSize; -end; - - -destructor TSFSMemoryChunkStream.Destroy (); -begin - if fFreeMem then FreeMem(fMemBuf); - inherited; -end; - - -function TSFSMemoryChunkStream.Seek (const offset: Int64; origin: TSeekOrigin): Int64; -begin - case origin of - soBeginning: result := offset; - soCurrent: result := offset+fCurPos; - soEnd: result := fMemSize+offset; - else raise XStreamError.Create('invalid Seek() call'); - end; - if (result < 0) then raise XStreamError.Create('invalid Seek() call'); - if (result > fMemSize) then result := fMemSize; - fCurPos := result; -end; - - -function TSFSMemoryChunkStream.Read (var buffer; count: LongInt): LongInt; -var - left: Integer; -begin - if (count < 0) then raise XStreamError.Create('negative read'); - left := fMemSize-fCurPos; - if (left < 0) then raise XStreamError.Create('internal error in TSFSMemoryChunkStream (read)'); - if (count > left) then count := left; - if (count > 0) then Move((fMemBuf+fCurPos)^, buffer, count); - Inc(fCurPos, count); - result := count; -end; - - -function TSFSMemoryChunkStream.Write (const buffer; count: LongInt): LongInt; -var - left: Integer; -begin - if (count < 0) then raise XStreamError.Create('negative write'); - left := fMemSize-fCurPos; - if (left < 0) then raise XStreamError.Create('internal error in TSFSMemoryChunkStream (write)'); - if (count > left) then count := left; - if (count > 0) then Move(buffer, (fMemBuf+fCurPos)^, count); - Inc(fCurPos, count); - result := count; -end; - - -end.