root/trunk/FlistFormat.pas

Revision 166, 61.8 kB (checked in by sagrer, 4 months ago)
  1. Частично реализована версия 2 формата FList-файлов. Пока что только чтение\запись + часть служебных функций + работает старый функционал, новые функции пока не реализованы, кроме варианта ковырять все руками клиентом класса.
  2. В формате поправлено место, способное вызвать исключение - добавлена обработка исключений туды, с возвратом false и сообщения о ошибке функцией, юзверь при желании может это дело обработать и вывести мессаг.
  3. Тест формата доработан на случай появления исключения.
  • Property svnmailer:content-charset set to cp1251
Line 
1 ///////////////////////////////////////////////////////////
2 //                     GGBuildTools                      //
3 //    Набор утилит для сборки проектов Gipat Group       //
4 //             Copyright (C) 2007 Gipat Group            //
5 //              Распространяется на условиях             //
6 //    Gipat Group's opened EI-editor-utility license     //
7 //                      версии 1.0                       //
8 //                                                       //
9 //                  www.gipatgroup.org                   //
10 ///////////////////////////////////////////////////////////
11
12 //К работе над данным файлом приложили руки, ноги.... короче аффтары:
13 // 1) Sagrer (sagrer@yandex.ru)
14
15 ////////////////////////////////////////////////////////////////////////
16
17 ////////////////////////////////////////////////////
18 //        Модуль для работы с файлами Flist       //
19 //       В этом файле хранится файловый список.   //
20 //      с дополнительной инфой вроде размера или  //
21 //                  чексуммы...                   //
22 //                                                //
23 //              версия формата - 2                //
24 ////////////////////////////////////////////////////
25
26 unit FlistFormat;
27
28 {$mode objfpc}{$H+}
29
30 interface
31
32 uses
33   Classes, SysUtils, Windows, LCLAnIniFile, crc, ExtraFileUtilsLcl, ExtraFunctionsLcl,
34   TranslManager;
35  
36 const
37   Flist_FormatVersion = 2;                //Номер версии формата в данном сырце.
38   //Дефолтные значения переменных класса....
39   Flist_Def_PathsAreAbsolute = false;           //По умолчанию использовать относительные пути.
40   Flist_Def_AddSize = true;                     //По умолчанию оставлять инфу о размере файла.
41   Flist_Def_AddCRC32 = true;                    //По умолчанию высчитывать и писать файлам CRC32.
42   Flist_Def_DoConsoleOutput = false;            //По умолчанию инфу в консоль не выводить.
43   Flist_Def_UseGroups = false;                  //По умолчанию группы не используем.
44   Flist_Def_UsedGroups = '';                    //По умолчанию список групп пуст.
45   //Тип файла
46   Flist_Filetype_File = 0;                //Тип файла - файл.
47   Flist_Filetype_Directory = 1;           //Тип файла - директория.
48   Flist_Filetype_Unknown = 2;             //Тип файла - хз что.
49   //Ответы CheckFile
50   Flist_CheckFile_Identical = 0;          //Файл идентичен.
51   Flist_CheckFile_MismatchedCRC32 = 1;    //Не совпадает CRC32.
52   Flist_CheckFile_MismatchedSize = 2;     //Не совпадает размер.
53   Flist_CheckFile_ElementNotExists = 3;   //Нет записи о файле в базе.
54   Flist_CheckFile_FileNotExists = 4;      //Файл на диске не существует.
55   Flist_CheckFile_NothingExists = 5;      //Не существует ни файла на диске ни записи о нем в массиве.
56   Flist_CheckFile_Error = 6;              //Ошибка. (какая - хз).
57   Flist_CheckFile_NonIdentical = 7;       //Не совпадает (чем - либо хз либо неважно).
58
59 type
60
61   RFlistFile = record                     //Структура для инфы о файле в списке.
62     FilePath : AnsiString;                //Путь к файлу.
63     FileType : byte;                      //Тип файла - Flist_Filetype_* .
64     FileSize : Int64;                     //Размер файла.
65     CRC32 : Cardinal;                     //CRC32 файла.
66     Group : AnsiString;                   //Группа файла.
67   end;
68  
69   RComparedElement = record               //Структура для инфы о проверенном элементе.
70     FilePath : AnsiString;                //Путь к файлу.
71     FileType : byte;                      //Тип файла - Flist_Filetype_* .
72     CheckResult : byte;                   //Результат сравнения списка и реального файла\папки.
73   end;
74  
75   AComparedList = array of RComparedElement;   //Тип массива с инфой о сравнении.
76
77   TFlistFormat = class                    //Класс для работы с файлами формата Flist (*.flist).
78   private
79     //Закрытые переменные
80     //[Main]
81     ReallyUseGroups : Boolean;            //Используются ли группы файлов.
82     //Закрытые методы.
83     function ParseFileType(const InputStr : AnsiString) : byte;       //Вернуть код типа файла по строковому значению типа.
84     function ReturnFileTypeStr(const InputCode : byte) : AnsiString;  //Наоборот сгенерить значение типа файла из кода.
85   public
86     //Переменные и методы чтения\записи
87     //[Main]
88     PathsAreAbsolute : Boolean;           //Используются ли абсолютные пути. Если false - то используются пути относительно места где лежит данный *.flist
89     AddSize : Boolean;                    //Добавлять ли инфу о размере файла.
90     AddCRC32 : Boolean;                   //Добавлять ли crc32 файла.
91     FilesNum : LongInt;                   //Количество файлов в списке.
92     function UseGroups : Boolean;         //Используются ли группы файлов - возвращает значение, записать прямо его нельзя.
93     UsedGroups : TStringList;             //Список используемых групп файлов, актуальность должна обеспечиваться реализацией, работающей с форматом, рекомендуется применять для оптимизации.
94     //[Files]
95     FileList : array of RFlistFile;       //Собсно массив с файлами (инфой про них).
96     //Служебные
97     ErrorMessage : AnsiString;            //Сообщение о ошибке если была
98     FileLoaded : boolean;                 //Если true - значит уже загружался какой-то файл.
99     LoadedFileName : AnsiString;          //Имя загруженного в последний раз файла.
100     GlobalRoot : AnsiString;              //Если тут пусто - глобальный корень та папка где лежит flist.
101     DoConsoleOutput : boolean;            //Если поставить true то по-возможности будет скидывать инфу о прогрессе в консоль.
102    
103     //Конструкторы-деструкторы...
104     constructor Create;
105     destructor Destroy; override;
106    
107     //Открытые методы...
108     procedure Clear();                                              //Очистить инфу класса.
109     function GetGlobalRoot() : AnsiString;                          //Вернуть корень.
110     function Load(const FileName : AnsiString) : boolean;           //Загрузить файл в класс.
111     function Save(const FileName : AnsiString) : boolean;           //Сохранить инфу из класса в файл.
112     function ElementNameExists(const ElementName : AnsiString) : boolean;   //Существует ли элемент с таким именем.
113     function GetElementNumber(const ElementName : AnsiString) : Integer;   //Какой номер у элемента с таким именем.
114     function FileExistsHere(const FileName : AnsiString) : boolean;     //Существует ли файл с таким именем.
115     function GetFileNumberHere(const FileName : AnsiString) : Integer;   //Какой номер у файла с таким именем.
116     function DirExistsHere(const DirName : AnsiString) : boolean;       //Существует ли дира с таким именем.
117     function GetDirNumberHere(const DirName : AnsiString) : Integer;   //Определить номер диры с таким именем.
118     function GetRealElementName(const ElemName : AnsiString) : AnsiString;   //Определить абсолютное имя для элемента
119     function GetRealElementName(const ElemNum : Integer) : AnsiString;   //Определить абсолютное имя для элемента с таким-то номером в списке.
120     function AddFile(const FileName : AnsiString) : boolean;        //Добавить файл.
121     function AddRealFile(const FileName : AnsiString) : boolean;    //Добавить реально существующий на диске файл (с подсчетом реальной инфы).
122     function AddDirectory(const DirName : AnsiString) : boolean;    //Добавить пустую диру.
123     function AddRealDirectory(const DirName : AnsiString; const Recursive : boolean) : boolean;  //Добавить реальную диру с подсчетом всей инфы по файлам.
124     function RemoveFile(const FileName : AnsiString) : boolean;     //Удалить файл из базы.
125     function RemoveDirectory(const DirName : AnsiString) : boolean;  //Удалить диру (со всеми входящими файлами и дирами).
126     function CheckFile(const ListFileName, RealFileName : AnsiString) : byte;   //Проверить насколько реальный файл соответствует инфе в файле. Возвращает значение одной из констант Flist_CheckFile_*. Можно сравнивать директории.
127     procedure CompareDirs(const ListDirName, RealDirName : AnsiString; var ComparedList : AComparedList);   //Сравнить 2 папки, результат записывается в ComparedList.
128     function CompareWithFlist(var OtherFlist : TFlistFormat; const ListDirName : AnsiString; var ComparedList : AComparedList) : boolean;   //Сравнить текущий файл с другим, считая его (этот другой файл) как будто за текущую версию файлов на венте.
129     function CompareWithFlist(const OtherFlistFile, ListDirName : AnsiString; var ComparedList : AComparedList) : boolean;   //Сравнить текущий файл с другим, считая его (этот другой файл) как будто за текущую версию файлов на венте.
130     function ConvertToSimpleComparedList(var ComparedList : AComparedList) : Boolean;  //Превратить список сравнения в нечно упрощенное - без лишней инфы про то как именно что различается, когда важен сам факт наличия различия.
131     procedure ParseUsedGroups(const GroupsStr : AnsiString);        //Пропарсить строку с группами и забить инфу в объект.
132     function GenUsedGroups : AnsiString;                            //Используя инфу в объекте сгенерить строку с группами.
133     procedure UpdateUsedGroups;                                     //Просмотреть список и сгенерировать новую инфу о группах, записать её в объект.
134   end;
135  
136 //Функции в прямом, открытом виде %).
137 function Flist_CheckFile_2Text(const Code : Byte) : AnsiString;    //Сгенерить текстовую строку из кода Flist_CheckFile_*
138
139 implementation
140
141 /////////////////////////////////////////////
142 //              TFlistFormat               //
143 /////////////////////////////////////////////
144
145 //-----------------------------------------//
146 //        Конструкторы-деструкторы...      //
147 //-----------------------------------------//
148
149 constructor TFlistFormat.Create;
150 begin
151   //Создать вложенные объекты классов...
152   UsedGroups := TStringList.Create;
153
154   //Проставить дефолтные значения...
155   Self.Clear();
156   ErrorMessage := 'none';
157   DoConsoleOutput := Flist_Def_DoConsoleOutput;
158 end;
159
160 destructor TFlistFormat.Destroy;
161 begin
162   //Выкидываем мусор
163   SetLength(FileList,0);
164   UsedGroups.Free;
165
166   //Выполнить унаследованный деструктор
167   inherited;
168 end;
169
170 //------------------------------------------//
171 //           Закрытые методы...             //
172 //------------------------------------------//
173
174 function TFlistFormat.ParseFileType(const InputStr : AnsiString) : byte;
175 //Вернуть код типа файла по строковому значению типа.
176 begin
177   if LowerCase(InputStr) = 'file' then begin
178     //Файл
179     Result := Flist_Filetype_File;
180   end
181   else if LowerCase(InputStr) = 'dir' then begin
182     //Дира
183     Result := Flist_Filetype_Directory;
184   end
185   else begin
186     //хз-что
187     Result := Flist_Filetype_Unknown;
188   end;
189 end;
190
191 function TFlistFormat.ReturnFileTypeStr(const InputCode : byte) : AnsiString;
192 //Наоборот сгенерить значение типа файла из кода.
193 begin
194   if InputCode = Flist_Filetype_File then begin
195     //Файл
196     Result := 'File';
197   end
198   else if InputCode = Flist_Filetype_Directory then begin
199     //Дира
200     Result := 'Dir';
201   end
202   else begin
203     //хз-что
204     Result := 'Unknown';
205   end;
206 end;
207
208 //------------------------------------------//
209 //           Открытые методы...             //
210 //------------------------------------------//
211
212 function TFlistFormat.UseGroups : Boolean;
213 //Используются ли группы файлов - возвращает значение, записать прямо его нельзя.
214 begin
215   Result := Self.ReallyUseGroups;
216 end;
217
218 procedure TFlistFormat.Clear();
219 //Очистить инфу класса.
220 begin
221   //Фактически - проставить дефолтные значения...
222   PathsAreAbsolute := Flist_Def_PathsAreAbsolute;
223   AddSize := Flist_Def_AddSize;
224   AddCRC32 := Flist_Def_AddCRC32;
225   FilesNum := 0;
226   ReallyUseGroups := Flist_Def_UseGroups;
227   Self.ParseUsedGroups(Flist_Def_UsedGroups);
228   //Ну и массив в 0 элементов
229   SetLength(FileList,0);
230   //Другая служебная инфа.
231   FileLoaded := false;
232   LoadedFileName := '';
233   GlobalRoot := '';
234 end;
235
236 function TFlistFormat.GetGlobalRoot() : AnsiString;
237 //Вернуть корень.
238 begin
239   if Self.GlobalRoot = '' then begin
240     //Если глобальный путь не указан.
241     if Self.FileLoaded = true then begin
242       //Если загружался какой-то файл - берем путь к этому файлу.
243       Result := ExtractFilePath(Self.LoadedFileName);
244     end
245     else begin
246       //Иначе берем путь к текущей дире.
247       Result := GetCurrentDir();
248     end;
249   end
250   else begin
251     //Если указан глобальный путь к корню - просто возвращаем его.
252     Result := Self.GlobalRoot;
253   end;
254 end;
255
256 function TFlistFormat.Load(const FileName : AnsiString) : boolean;
257 //Загрузить инфу файла в класс.
258 var
259   AnIniFile : TAnIniFile;
260   AllOk : boolean;
261   IniValueList : AIniValueList;
262   I, J, ReadVersion : Integer;
263  
264 begin
265   //Инициализация.
266   AnIniFile := TAnIniFile.Create;
267   SetLength(IniValueList,0,0);  //Инициализация пустого динамического массива для инфы секции [Files]
268   Result := false;
269   if Self.DoConsoleOutput = true then begin
270     Writeln(_('Loading file ')+FileName+' ... ');
271   end;
272  
273   //Очистка инфы класса...
274   Self.Clear();
275  
276   //Собсно грузим...
277   AllOk := FileExists(FileName);
278   if AllOk = false then begin
279     //Если файла не существует...
280     ErrorMessage := _('File ')+FileName+_(' is not exists.');
281   end
282   else begin
283     //Если сам файлик вообще существует... Загружаем...
284     AllOk := AnIniFile.Load(FileName);
285     if AllOk = false then begin
286       //Не удалось загрузить файл.
287       ErrorMessage := _('Error, loading file ')+FileName;
288     end;
289   end;
290  
291   if AllOk = true then begin
292     //Ок, загружено. Читаем сигнатуру...
293     if AnIniFile.ReadString('Main', 'FormatName') <> 'FileList' then begin
294       //Это не FileList-файл.
295       AllOk := false;
296       ErrorMessage := _('File ')+FileName+_(' is not FileList file.');
297     end;
298   end;
299
300   if AllOk = true then begin
301     //Проверяем версию формата...
302     ReadVersion := AnIniFile.ReadInteger('Main', 'FormatVersion');
303     if ReadVersion > Flist_FormatVersion then begin
304       //Формат более новой версии чем код в бинарнике.
305       AllOk := false;
306       ErrorMessage := _('File ')+FileName+_(' has more new version (')+IntToStr(ReadVersion)+_(') then i know (')+IntToStr(Flist_FormatVersion)+_('). Aborted loading. Try update to a new version of this software.');
307     end
308     else if ReadVersion < Flist_FormatVersion then begin
309       //Загруженный файлик более старой версии - надо его обновить.
310       if ReadVersion = 1 then begin
311         // 1 -> 2.
312         //[Main]
313         AnIniFile.WriteBool('Main','UseGroups',Flist_Def_UseGroups);
314         AnIniFile.WriteString('Main','UsedGroups',Flist_Def_UsedGroups);
315         AnIniFile.WriteInteger('Main', 'FormatVersion',2);
316       end;
317     end;
318   end;
319  
320   if AllOk = true then begin
321     //Собсно читаем инфу...
322
323     //[Main]
324     PathsAreAbsolute := AnIniFile.ReadBool('Main','PathsAreAbsolute');
325     AddSize := AnIniFile.ReadBool('Main','AddSize');
326     AddCRC32 := AnIniFile.ReadBool('Main','AddCRC32');
327     //FilesNum := AnIniFile.ReadInteger('Main','FilesNum');      //Ненужно, важнее реальное количество в списке.
328     ReallyUseGroups := AnIniFile.ReadBool('Main','UseGroups');
329     Self.ParseUsedGroups(AnIniFile.ReadString('Main','UsedGroups'));
330     //[Files]
331     AnIniFile.ReadIniValueList('Files',IniValueList);   //Читаем секцию...
332     //Заносим инфу о количестве файлов...
333     FilesNum := Length(IniValueList);
334     //Выделяем память под файлы...
335     SetLength(FileList,FilesNum);
336     //И собсно парсим...
337     if FilesNum > 0 then begin
338       //Если собсно есть что парсить.
339       for I := 0 to FilesNum-1 do begin
340         for J := 0 to Length(IniValueList[I])-1 do begin
341           //По элементам.
342           if IniValueList[I,J].Name = 'File' then begin
343             FileList[I].FilePath := IniValueList[I,J].Value;
344           end
345           else if IniValueList[I,J].Name = 'Type' then begin
346             FileList[I].FileType := ParseFileType(IniValueList[I,J].Value);
347           end
348           else if IniValueList[I,J].Name = 'Size' then begin
349             FileList[I].FileSize := StrToInt(IniValueList[I,J].Value);
350           end
351           else if IniValueList[I,J].Name = 'CRC32' then begin
352             FileList[I].CRC32 := StrToInt('$'+IniValueList[I,J].Value);     //Знак доллара - т.к. читается число в виде HEX.
353           end
354           else if IniValueList[I,J].Name = 'Group' then begin
355             FileList[I].Group := IniValueList[I,J].Value;
356           end
357         end;
358         //Если используются группы - убедиться что файлу пробита группа.
359         if Self.UseGroups = true then begin
360           if Length(FileList[I].Group) = 0 then begin
361             //Пусто - вбиваем "all"
362             FileList[I].Group := 'all';
363           end;
364         end;
365       end;
366     end;
367
368     //Ну, вроде бы все что надо прочитано. Теперь отмечаем что фсеок и собсно все.
369     Result := true;
370     if Self.DoConsoleOutput = true then begin
371       Writeln(_('Loaded file ')+FileName);
372     end;
373     FileLoaded := true;
374     LoadedFileName := FileName;
375   end;
376  
377   //Уборка мусора.
378   AnIniFile.Free;
379   SetLength(IniValueList,0,0);
380 end;
381
382 function TFlistFormat.Save(const FileName : AnsiString) : boolean;
383 //Сохранить инфу из класса в файл.
384 var
385   AnIniFile : TAnIniFile;
386   I, J, ValElemNum : Integer;
387   IniValueList : AIniValueList;
388  
389 begin
390   //Инициализация.
391   AnIniFile := TAnIniFile.Create;
392   SetLength(IniValueList,0,0);
393   Result := false;
394   if Self.DoConsoleOutput = true then begin
395     Writeln(_('Saving file ')+FileName+' ... ');
396   end;
397  
398   //Крейтим новое файло...
399   AnIniFile.MakNewFile;
400   //Закидываем инфу...
401   //[Main]
402   AnIniFile.WriteString('Main', 'FormatName', 'FileList');
403   AnIniFile.WriteInteger('Main', 'FormatVersion', Flist_FormatVersion);
404   AnIniFile.WriteBool('Main', 'PathsAreAbsolute', PathsAreAbsolute);
405   AnIniFile.WriteBool('Main', 'AddSize', AddSize);
406   AnIniFile.WriteBool('Main', 'AddCRC32', AddCRC32);
407   AnIniFile.WriteInteger('Main', 'FilesNum', FilesNum);
408   AnIniFile.WriteBool('Main', 'UseGroups', UseGroups);
409   AnIniFile.WriteString('Main', 'UsedGroups', GenUsedGroups);
410  
411   //[Files]
412   //Определить сколько элементов будет в каждой "строке" IniValueList
413   ValElemNum := 2;      //Минимум.
414   if AddSize = true then ValElemNum := ValElemNum+1;
415   if AddCRC32 = true then ValElemNum := ValElemNum+1;
416   if UseGroups = true then ValElemNum := ValElemNum+1;
417   //Выделить память под элементы IniValueList
418   SetLength(IniValueList,FilesNum,ValElemNum);
419   //Закинуть всю инфу по файлам в IniValueList
420   for I := 0 to FilesNum-1 do begin
421     J := 0;
422     //File
423     IniValueList[I,J].Name := 'File';
424     IniValueList[I,J].Value := FileList[I].FilePath;
425     J := J+1;
426     //Type
427     IniValueList[I,J].Name := 'Type';
428     IniValueList[I,J].Value := ReturnFileTypeStr(FileList[I].FileType);
429     J := J+1;
430     //Size
431     if AddSize = true then begin
432       IniValueList[I,J].Name := 'Size';
433       IniValueList[I,J].Value := IntToStr(FileList[I].FileSize);
434       J := J+1;
435     end;
436     //CRC32
437     if AddCRC32 = true then begin
438       IniValueList[I,J].Name := 'CRC32';
439       IniValueList[I,J].Value := IntToHex(FileList[I].CRC32,8);
440     end;
441     //Group
442     if UseGroups = true then begin
443       IniValueList[I,J].Name := 'Group';
444       IniValueList[I,J].Value := FileList[I].Group;
445     end;
446   end;
447   //Записать сгенеренный список в секцию...
448   AnIniFile.WriteIniValueList('Files',IniValueList);
449  
450   //Терь можно сохранить файл...
451   Result := AnIniFile.Save(FileName);
452   //Если сохранилось удачно - запоминаем чего там за файлег.
453   if Result = true then begin
454     FileLoaded := true;
455     LoadedFileName := FileName;
456     if Self.DoConsoleOutput = true then begin
457       Writeln(_('Saved file ')+FileName);
458     end;
459   end;
460  
461   //Выкидываем хлам (ака мусор).
462   AnIniFile.Free;
463   SetLength(IniValueList,0,0);
464 end;
465
466 function TFlistFormat.ElementNameExists(const ElementName : AnsiString) : boolean;
467 //Существует ли элемент с таким именем.
468 var
469   I : Integer;
470   TempElemName : AnsiString;
471  
472 begin
473   //Инициализация.
474   Result := false;
475   I := -1;
476   TempElemName := UpperCase(DelLastSlash(ElementName));
477  
478   //Пробежаться по массиву с файлами пока элемент не будет найден или пока массив не закончится.
479   repeat
480  
481     I := I+1;
482     //Итак, независимо от того что это - папка ли, файл ли - смотрим.
483     if UpperCase(DelLastSlash(FileList[I].FilePath)) = TempElemName then begin
484       Result := true;     //Все, нашли, из цикла оно терь вылетит и вернется true.
485     end;
486    
487   until (Result = true) or (I = FilesNum-1);
488  
489 end;
490
491 function TFlistFormat.GetElementNumber(const ElementName : AnsiString) : Integer;
492 //Какой номер у элемента с таким именем.
493 var
494   I : Integer;
495   TempElemName : AnsiString;
496
497 begin
498   //Инициализация.
499   Result := -1;
500   I := -1;
501   TempElemName := UpperCase(DelLastSlash(ElementName));
502
503   //Пробежаться по массиву с файлами пока элемент не будет найден или пока массив не закончится.
504   repeat
505
506     I := I+1;
507     //Итак, независимо от того что это - папка ли, файл ли - смотрим.
508     if UpperCase(DelLastSlash(FileList[I].FilePath)) = TempElemName then begin
509       Result := I;     //Все, нашли, из цикла оно терь вылетит и вернется true.
510     end;
511
512   until (Result = I) or (I = FilesNum-1);
513
514 end;
515
516 function TFlistFormat.FileExistsHere(const FileName : AnsiString) : boolean;
517 //Существует ли файл с таким именем.
518 var
519   I : Integer;
520   TempFileName : AnsiString;
521  
522 begin
523   //Инициализация.
524   Result := false;
525   I := -1;
526   TempFileName := UpperCase(FileName);
527
528   //Пробежаться по массиву с файлами пока элемент не будет найден или пока массив не закончится.
529   repeat
530
531     I := I+1;
532    
533     if FileList[I].FileType = Flist_Filetype_File then begin
534       //Только если это файло.
535       if UpperCase(FileList[I].FilePath) = TempFileName then begin
536         Result := true;     //Все, нашли, из цикла оно терь вылетит и вернется true.
537       end;
538     end;
539
540   until (Result = true) or (I = FilesNum-1);
541
542 end;
543
544 function TFlistFormat.GetFileNumberHere(const FileName : AnsiString) : Integer;
545 //Какой номер у файла с таким именем.
546 var
547   I : Integer;
548   TempFileName : AnsiString;
549
550 begin
551   //Инициализация.
552   Result := -1;
553   I := -1;
554   TempFileName := UpperCase(FileName);
555
556   //Пробежаться по массиву с файлами пока элемент не будет найден или пока массив не закончится.
557   repeat
558
559     I := I+1;
560
561     if FileList[I].FileType = Flist_Filetype_File then begin
562       //Только если это файло.
563       if UpperCase(FileList[I].FilePath) = TempFileName then begin
564         Result := I;     //Все, нашли, из цикла оно терь вылетит и вернется true.
565       end;
566     end;
567
568   until (Result = I) or (I = FilesNum-1);
569
570 end;
571
572 function TFlistFormat.DirExistsHere(const DirName : AnsiString) : boolean;
573 //Существует ли дира с таким именем.
574 var
575   I : Integer;
576   TempDirName : AnsiString;
577
578 begin
579   //Инициализация.
580   Result := false;
581   I := -1;
582   TempDirName := DelLastSlash(UpperCase(DirName));
583
584   //Пробежаться по массиву с файлами пока элемент не будет найден или пока массив не закончится.
585   repeat
586
587     I := I+1;
588
589     if FileList[I].FileType = Flist_Filetype_Directory then begin
590       //Только если это дира.
591       if UpperCase(DelLastSlash(FileList[I].FilePath)) = TempDirName then begin
592         Result := true;     //Все, нашли, из цикла оно терь вылетит и вернется true.
593       end;
594     end;
595
596   until (Result = true) or (I = FilesNum-1);
597
598 end;
599
600 function TFlistFormat.GetDirNumberHere(const DirName : AnsiString) : Integer;
601 //Определить номер диры с таким именем.
602 var
603   I : Integer;
604   TempDirName : AnsiString;
605
606 begin
607   //Инициализация.
608   Result := -1;
609   I := -1;
610   TempDirName := DelLastSlash(UpperCase(DirName));
611
612   //Пробежаться по массиву с файлами пока элемент не будет найден или пока массив не закончится.
613   repeat
614
615     I := I+1;
616
617     if FileList[I].FileType = Flist_Filetype_Directory then begin
618       //Только если это дира.
619       if UpperCase(DelLastSlash(FileList[I].FilePath)) = TempDirName then begin
620         Result := I;     //Все, нашли, из цикла оно терь вылетит и вернется true.
621       end;
622     end;
623
624   until (Result = I) or (I = FilesNum-1);
625
626 end;
627
628 function TFlistFormat.GetRealElementName(const ElemName : AnsiString) : AnsiString;
629 //Определить абсолютное имя для элемента
630 begin
631   //Получаем абсолютный путь....
632   if IsLocalPath(ElemName) = true then begin
633     //Если надо генерить - генерим.
634     Result := ResolveLocalPath(ElemName,Self.GetGlobalRoot());
635   end
636   else begin
637     //Он уже глобальный.
638     Result := ElemName;
639   end;
640 end;
641
642 function TFlistFormat.GetRealElementName(const ElemNum : Integer) : AnsiString;
643 //Определить абсолютное имя для элемента с таким-то номером в списке.
644 begin
645   //Просто вызываем функцию выше с именем элемента...
646   Result := GetRealElementName(Self.FileList[ElemNum].FilePath);
647 end;
648
649 function TFlistFormat.AddFile(const FileName : AnsiString) : boolean;
650 //Добавить файл.
651 var
652   CurrFileNum : Integer;
653   LocalName, GlobalName : AnsiString;
654  
655 begin
656   //Инициализация.
657   Result := false;
658   CurrFileNum := FilesNum;   //После добавления нового файла эта цифра будет номером последнего элемента массива.
659  
660   //Увеличить размер массива на один.
661   FilesNum := FilesNum+1;
662   SetLength(FileList,FilesNum);
663  
664   //Получаем абсолютный путь....
665   if IsLocalPath(FileName) = true then begin
666     //Если надо генерить - генерим.
667     GlobalName := ResolveLocalPath(FileName,Self.GetGlobalRoot());
668   end
669   else begin
670     //Он уже глобальный.
671     GlobalName := FileName;
672   end;
673
674   //Если нужно - получаем относительный путь.
675   if Self.PathsAreAbsolute = false then begin
676     LocalName := GetLocalPath(Self.GetGlobalRoot(),GlobalName);
677   end;
678  
679   //Забить параметры для файла.
680   //FilePath
681   if Self.PathsAreAbsolute = true then begin
682     //Имя должно быть абсолютное.
683     FileList[CurrFileNum].FilePath := GlobalName;
684   end
685   else begin
686     //Имя должно быть относительное
687     FileList[CurrFileNum].FilePath := LocalName;
688   end;
689   //FileType
690   FileList[CurrFileNum].FileType := Flist_Filetype_File;
691   //FileSize
692   if Self.AddSize = true then begin
693     FileList[CurrFileNum].FileSize := 0;
694   end;
695   //CRC32
696   if Self.AddCRC32 = true then begin
697     FileList[CurrFileNum].CRC32 := crc32(0, nil, 0);         //Получится "пустое" значение CRC32.
698   end;
699  
700   //Вернуть результат.
701   Result := true;
702 end;
703
704 function TFlistFormat.AddRealFile(const FileName : AnsiString) : boolean;
705 //Добавить реально существующий на диске файл (с подсчетом реальной инфы).
706 var
707   CurrFileNum : Integer;
708   LocalName, GlobalName : AnsiString;
709  
710 begin
711   //Код независит от функции TFlistFormat.AddFile в целях более оптимальной работы.
712   //Инициализация.
713   Result := false;
714   CurrFileNum := FilesNum;   //После добавления нового файла эта цифра будет номером последнего элемента массива.
715  
716   //Получаем абсолютный путь....
717   if IsLocalPath(FileName) = true then begin
718     //Если надо генерить - генерим.
719     GlobalName := ResolveLocalPath(FileName,Self.GetGlobalRoot());
720   end
721   else begin
722     //Он уже глобальный.
723     GlobalName := FileName;
724   end;
725  
726   //Если нужно - получаем относительный путь.
727   if Self.PathsAreAbsolute = false then begin
728     LocalName := GetLocalPath(Self.GetGlobalRoot(),GlobalName);
729   end;
730
731   //Увеличить размер массива на один.
732   FilesNum := FilesNum+1;
733   SetLength(FileList,FilesNum);
734
735   //Забить параметры для файла.
736   //FilePath
737   if Self.PathsAreAbsolute = true then begin
738     //Имя должно быть абсолютное.
739     FileList[CurrFileNum].FilePath := GlobalName;
740   end
741   else begin
742     //Имя должно быть относительное
743     FileList[CurrFileNum].FilePath := LocalName;
744   end;
745   //FileType
746   FileList[CurrFileNum].FileType := Flist_Filetype_File;
747   //FileSize
748   if Self.AddSize = true then begin
749     FileList[CurrFileNum].FileSize := GetFileSize(GlobalName);       //Подсчитать размер. Если файла нету - вернет нолик.
750   end;
751   //CRC32
752   if Self.AddCRC32 = true then begin
753     FileList[CurrFileNum].CRC32 := FileToCRC32(GlobalName,DoConsoleOutput);    //Подсчитать CRC32 для файла. Если файла нету - будет "пустое" значение.
754   end;
755
756   //Вернуть результат.
757   Result := true;
758 end;
759
760 function TFlistFormat.AddDirectory(const DirName : AnsiString) : boolean;
761 //Добавить пустую диру.
762 var
763   CurrFileNum : Integer;
764   LocalName, GlobalName : AnsiString;
765
766 begin
767   //Инициализация.
768   Result := false;
769   CurrFileNum := FilesNum;   //После добавления новой диры эта цифра будет номером последнего элемента массива.
770
771   //Увеличить размер массива на один.
772   FilesNum := FilesNum+1;
773   SetLength(FileList,FilesNum);
774  
775   //Получаем абсолютный путь....
776   if IsLocalPath(DirName) = true then begin
777     //Если надо генерить - генерим.
778     GlobalName := DelLastSlash(ResolveLocalPath(DirName,Self.GetGlobalRoot()));
779   end
780   else begin
781     //Он уже глобальный.
782     GlobalName := DelLastSlash(DirName);
783   end;
784
785   //Если нужно - получаем относительный путь.
786   if Self.PathsAreAbsolute = false then begin
787     LocalName := GetLocalPath(Self.GetGlobalRoot(),GlobalName);
788   end;
789
790   //Забить параметры для диры.
791   //FilePath
792   if Self.PathsAreAbsolute = true then begin
793     //Имя должно быть абсолютное.
794     FileList[CurrFileNum].FilePath := GlobalName;
795   end
796   else begin
797     //Имя должно быть относительное
798     FileList[CurrFileNum].FilePath := LocalName;
799   end;
800   //FileType
801   FileList[CurrFileNum].FileType := Flist_Filetype_Directory;
802
803   //Вернуть результат.
804   Result := true;
805 end;
806
807 function TFlistFormat.AddRealDirectory(const DirName : AnsiString; const Recursive : boolean) : boolean;
808 //Добавить реальную диру с подсчетом всей инфы по файлам.
809 var
810   AddingFiles : LongInt;
811   LocalName, GlobalName : AnsiString;
812  
813   function ReallyAddDirectory(const DirName : AnsiString; const Recursive : boolean) : boolean;
814   //Функция, непосредственно читающая инфу по директории...
815   var
816     SearchRec : TSearchRec;
817     LocalDirName : AnsiString;
818   begin
819     //Инициализация.
820     Result := true;
821    
822     //Если нужно - получаем относительный путь.
823     if Self.PathsAreAbsolute = false then begin
824       LocalDirName := GetLocalPath(Self.GetGlobalRoot(),DirName);
825     end;
826    
827     //Начать сбор инфы...
828     if FindFirst(AddLastSlash(DirName)+'*', faAnyFile and faDirectory, SearchRec) = 0 then begin
829       //Если поиск успешно начат.
830       repeat
831         //Повторяем поиск пока не закончится то что можно искать %).
832         if (SearchRec.Name <> '.') and (SearchRec.Name <> '..') then begin
833           //Чего-то нашли.
834           if (SearchRec.Attr and faDirectory) <> faDirectory then begin
835             //Ход мыслей прост. Если то что мы нашли не директория - значит это файл %). Ибо проверять
836             //по принципу "файл ли это" опасно - директория - она тоже файл %).
837             //Итак, то что нашли - файл. Добавляем.
838
839             //Забить параметры для файла.
840             //FilePath
841             if Self.PathsAreAbsolute = true then begin
842               //Имя должно быть абсолютное.
843               FileList[FilesNum].FilePath := AddLastSlash(DirName)+SearchRec.Name;
844             end
845             else begin
846               //Имя должно быть относительное
847               FileList[FilesNum].FilePath := AddLastSlash(LocalDirName)+SearchRec.Name;
848             end;
849             //FileType
850             FileList[FilesNum].FileType := Flist_Filetype_File;
851             //FileSize
852             if Self.AddSize = true then begin
853               try
854                 FileList[FilesNum].FileSize := GetFileSize(AddLastSlash(DirName)+SearchRec.Name);       //Подсчитать размер. Если файла нету - вернет нолик.
855               except
856                 Self.ErrorMessage := _('Can''t calc file size for ')+AddLastSlash(DirName)+SearchRec.Name;
857                 Result := false;
858                 Break;
859               end;
860             end;
861             //CRC32
862             if Self.AddCRC32 = true then begin
863               try
864                 FileList[FilesNum].CRC32 := FileToCRC32(AddLastSlash(DirName)+SearchRec.Name,DoConsoleOutput);    //Подсчитать CRC32 для файла. Если файла нету - будет "пустое" значение.
865               except
866                 Self.ErrorMessage := _('Can''t calc CRC32 for ')+AddLastSlash(DirName)+SearchRec.Name;
867                 Result := false;
868                 Break;
869               end;
870             end;
871            
872             //+1 счетчик файлов в массиве с инфой.
873             FilesNum := FilesNum+1;
874            
875           end
876           else begin
877             //Если то что нашли - директория. Аналогично - добавляем...
878
879             //Забить параметры для диры.
880             //FilePath
881             if Self.PathsAreAbsolute = true then begin
882               //Имя должно быть абсолютное.
883               FileList[FilesNum].FilePath := AddLastSlash(DirName)+SearchRec.Name;
884             end
885             else begin
886               //Имя должно быть относительное
887               FileList[FilesNum].FilePath := AddLastSlash(LocalDirName)+SearchRec.Name;
888             end;
889             //FileType
890             FileList[FilesNum].FileType := Flist_Filetype_Directory;
891            
892             //+1 счетчик файлов в массиве с инфой.
893             FilesNum := FilesNum+1;
894
895             //И если нужно - вызвать функцию рекурсивно.
896             if Recursive = true then begin
897               Result := ReallyAddDirectory(AddLastSlash(DirName)+SearchRec.Name, Recursive);
898               if Result = false then begin
899                 //Был какой-то ошибк. Брякаем.
900                 Break;
901               end;
902             end;
903           end;
904         end;
905        
906       until FindNext(SearchRec) <> 0;
907      
908       //Закрыть поиск.
909       SysUtils.FindClose(SearchRec);
910     end;
911   end;
912  
913 begin
914   //Функция может работать "рекурсивно". На самом деле прямой рекурсии этой функции даже в этом случае
915   //не будет - рекурситься будут только "вложенные" функции. В любом случае - сначала подсчитывает
916   //количество добавляемых файлов (либо рекурсивно либо нет, смотря что требуется), потом выделяется память
917   //под массив, потом вызывается функция добавления данной папки, которая уже может быть рекурсивной если
918   //это необходимо. Таким образом размер динамического массива определяется однократно, что позволяет не
919   //заниматься копированием памяти каждый раз когда надо увеличить массив. Рекурсивная функция уже точно знает
920   //сколько файлов будет добавлено и размер массива не трогает - только читает и добавляет инфу.
921  
922   //Инициализация.
923   Result := false;
924  
925   //Получаем абсолютный путь....
926   if IsLocalPath(DirName) = true then begin
927     //Если надо генерить - генерим.
928     GlobalName := DelLastSlash(ResolveLocalPath(DirName,Self.GetGlobalRoot()));
929   end
930   else begin
931     //Он уже глобальный.
932     GlobalName := DelLastSlash(DirName);
933   end;
934
935   //Если нужно - получаем относительный путь.
936   if Self.PathsAreAbsolute = false then begin
937     LocalName := GetLocalPath(Self.GetGlobalRoot(),GlobalName);
938   end;
939  
940   //Итак, во-первых подсчитать количество добавляемых файлов...
941   AddingFiles := GetFilesNumber(GlobalName,Recursive)+1;       //+1 - данная конкретная дира которая DirName
942   //Теперь выставить правильный размер массива... FilesNum не трогаем - это в принципе счетчик тут.
943   SetLength(FileList,FilesNum+AddingFiles);
944
945   //Добавить текущую диру.
946   //FilePath
947   if Self.PathsAreAbsolute = true then begin
948     //Имя должно быть абсолютное.
949     FileList[FilesNum].FilePath := GlobalName;
950   end
951   else begin
952     //Имя должно быть относительное
953     FileList[FilesNum].FilePath := LocalName;
954   end;
955   //FileType
956   FileList[FilesNum].FileType := Flist_Filetype_Directory;
957   //+1 количество файлов в инфе.
958   FilesNum := FilesNum+1;
959  
960   //Ок. Можно звать функцию для непосредственного чтения инфы директории, если надо то она и будет рекурситься.
961   Result := ReallyAddDirectory(GlobalName, Recursive);
962  
963 end;
964
965 function TFlistFormat.RemoveFile(const FileName : AnsiString) : boolean;
966 //Удалить файл из базы.
967 var
968   RealSearchPath, TempStr : AnsiString;
969   Finded : boolean;
970   I, J : Integer;
971   NewFileList : array of RFlistFile;
972   DeletingElem, DummyElem : RFlistFile;
973  
974 begin
975   //Инициализация
976   Result := false;
977  
978   //Смотрим что там за путь в аргументе и если надо приводим его к тому виду как они в массиве.
979   if Self.PathsAreAbsolute = true then begin
980     //Если нужны абсолютные пути.
981     if IsLocalPath(FileName) = true then begin
982       //Если надо генерить - генерим.
983       RealSearchPath := ResolveLocalPath(FileName,Self.GetGlobalRoot());
984     end
985     else begin
986       //Он уже глобальный.
987       RealSearchPath := FileName;
988     end;
989   end
990   else begin
991     //Если нужны относительные пути.
992     if IsLocalPath(FileName) = true then begin
993       //Он уже локальный.
994       RealSearchPath := FileName;
995     end
996     else begin
997       //Генерим локальный
998       RealSearchPath := GetLocalPath(Self.GetGlobalRoot(),FileName);
999     end;
1000   end;
1001
1002   //Ищем файл с таким локальным\глобальным именем и удаляем его из массива.
1003   Finded := false;
1004   I := -1;
1005   repeat
1006     I := I+1;
1007     if UpperCase(Self.FileList[I].FilePath) = UpperCase(RealSearchPath) then begin
1008       //Нашли.
1009       Finded := true;
1010     end;
1011   until (Finded = true) or (I = FilesNum-1);
1012  
1013   if Finded = true then begin
1014     //Если нашли чего удалять, а удаляемый элемент не последний - двигаем элементы внутрях массива.
1015     if I <> (FilesNum-1) then begin
1016       Move(Self.FileList[I],DeletingElem,SizeOf(RFlistFile));    //Временно копируем инфу удаляемого элемента.
1017       Move(Self.FileList[I+1],Self.FileList[I],SizeOf(RFlistFile)*(FilesNum-(I+1)));    //Переносим инфу других элементов.
1018       Move(DeletingElem,Self.FileList[FilesNum-1],SizeOf(RFlistFile));     //Копируем удаляемый элемент в конец массива чтоб его прочистило при смене размера массива (во избежание утечков памяти).
1019       Move(DummyElem,DeletingElem,SizeOf(RFlistFile));     //Копируем во временный элемент инфу элемента заглушки - чтобы тупой сборщег мусора не полез по ссылкам и не заховал последний элемент массива раньше или позже (т.е. еще раз на пустое место) правильного времени.
1020     end;
1021     //Прочистить удаляемый элемент массива.
1022     Self.FileList[FilesNum-1].CRC32 := 0;
1023     Self.FileList[FilesNum-1].FilePath := '';
1024     Self.FileList[FilesNum-1].FileSize := 0;
1025     Self.FileList[FilesNum-1].FileType := 0;
1026     //И почистить массив.
1027     FilesNum := FilesNum-1;
1028     SetLength(Self.FileList,FilesNum);
1029     //Done.
1030     Result := true;
1031   end;
1032 end;
1033
1034 function TFlistFormat.RemoveDirectory(const DirName : AnsiString) : boolean;
1035 //Удалить диру (со всеми входящими файлами и дирами).
1036 var
1037   I : Integer;
1038   RealSearchPath : AnsiString;
1039  
1040 begin
1041   //Принцип действия следующий: двигаемся по массиву, если обнаруживаем child-а (в т.ч. и сама эта дира
1042   //попадет в этот фильтр - явно её удалять и не надо) - то удаляем вышенаписанной функцией, счетчик
1043   //отщелкиваем назад и идем дальше.
1044  
1045   //Инициализация.
1046   Result := false;
1047  
1048   //Смотрим что там за путь в аргументе и если надо приводим его к тому виду как они в массиве.
1049   if Self.PathsAreAbsolute = true then begin
1050     //Если нужны абсолютные пути.
1051     if IsLocalPath(DirName) = true then begin
1052       //Если надо генерить - генерим.
1053       RealSearchPath := ResolveLocalPath(DirName,Self.GetGlobalRoot());
1054     end
1055     else begin
1056       //Он уже глобальный.
1057       RealSearchPath := DirName;