root/branches/0_1_3/FlistFormat.pas

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