/////////////////////////////////////////////////////////// // GGBuildTools // // Набор утилит для сборки проектов Gipat Group // // Copyright (C) 2007 Gipat Group // // Распространяется на условиях // // Gipat Group's opened EI-editor-utility license // // версии 1.0 // // // // www.gipatgroup.org // /////////////////////////////////////////////////////////// //К работе над данным файлом приложили руки, ноги.... короче аффтары: // 1) Sagrer (sagrer@yandex.ru) //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////// // Модуль для работы с файлами Flist // // В этом файле хранится файловый список. // // с дополнительной инфой вроде размера или // // чексуммы... // // // // версия формата - 2 // //////////////////////////////////////////////////// unit FlistFormat; {$mode objfpc}{$H+} interface uses Classes, SysUtils, Windows, LCLAnIniFile, crc, ExtraFileUtilsLcl, ExtraFunctionsLcl, TranslManager; const Flist_FormatVersion = 2; //Номер версии формата в данном сырце. //Дефолтные значения переменных класса.... Flist_Def_PathsAreAbsolute = false; //По умолчанию использовать относительные пути. Flist_Def_AddSize = true; //По умолчанию оставлять инфу о размере файла. Flist_Def_AddCRC32 = true; //По умолчанию высчитывать и писать файлам CRC32. Flist_Def_DoConsoleOutput = false; //По умолчанию инфу в консоль не выводить. Flist_Def_UseGroups = false; //По умолчанию группы не используем. Flist_Def_UsedGroups = ''; //По умолчанию список групп пуст. //Тип файла Flist_Filetype_File = 0; //Тип файла - файл. Flist_Filetype_Directory = 1; //Тип файла - директория. Flist_Filetype_Unknown = 2; //Тип файла - хз что. //Ответы CheckFile Flist_CheckFile_Identical = 0; //Файл идентичен. Flist_CheckFile_MismatchedCRC32 = 1; //Не совпадает CRC32. Flist_CheckFile_MismatchedSize = 2; //Не совпадает размер. Flist_CheckFile_ElementNotExists = 3; //Нет записи о файле в базе. Flist_CheckFile_FileNotExists = 4; //Файл на диске не существует. Flist_CheckFile_NothingExists = 5; //Не существует ни файла на диске ни записи о нем в массиве. Flist_CheckFile_Error = 6; //Ошибка. (какая - хз). Flist_CheckFile_NonIdentical = 7; //Не совпадает (чем - либо хз либо неважно). type RFlistFile = record //Структура для инфы о файле в списке. FilePath : AnsiString; //Путь к файлу. FileType : byte; //Тип файла - Flist_Filetype_* . FileSize : Int64; //Размер файла. CRC32 : Cardinal; //CRC32 файла. Group : AnsiString; //Группа файла. end; RComparedElement = record //Структура для инфы о проверенном элементе. FilePath : AnsiString; //Путь к файлу. FileType : byte; //Тип файла - Flist_Filetype_* . CheckResult : byte; //Результат сравнения списка и реального файла\папки. end; AComparedList = array of RComparedElement; //Тип массива с инфой о сравнении. TFlistFormat = class //Класс для работы с файлами формата Flist (*.flist). private //Закрытые переменные //[Main] ReallyUseGroups : Boolean; //Используются ли группы файлов. //Закрытые методы. function ParseFileType(const InputStr : AnsiString) : byte; //Вернуть код типа файла по строковому значению типа. function ReturnFileTypeStr(const InputCode : byte) : AnsiString; //Наоборот сгенерить значение типа файла из кода. public //Переменные и методы чтения\записи //[Main] PathsAreAbsolute : Boolean; //Используются ли абсолютные пути. Если false - то используются пути относительно места где лежит данный *.flist AddSize : Boolean; //Добавлять ли инфу о размере файла. AddCRC32 : Boolean; //Добавлять ли crc32 файла. FilesNum : LongInt; //Количество файлов в списке. function UseGroups : Boolean; //Используются ли группы файлов - возвращает значение, записать прямо его нельзя. UsedGroups : TStringList; //Список используемых групп файлов, актуальность должна обеспечиваться реализацией, работающей с форматом, рекомендуется применять для оптимизации. //[Files] FileList : array of RFlistFile; //Собсно массив с файлами (инфой про них). //Служебные ErrorMessage : AnsiString; //Сообщение о ошибке если была FileLoaded : boolean; //Если true - значит уже загружался какой-то файл. LoadedFileName : AnsiString; //Имя загруженного в последний раз файла. GlobalRoot : AnsiString; //Если тут пусто - глобальный корень та папка где лежит flist. DoConsoleOutput : boolean; //Если поставить true то по-возможности будет скидывать инфу о прогрессе в консоль. //Конструкторы-деструкторы... constructor Create; destructor Destroy; override; //Открытые методы... procedure Clear(); //Очистить инфу класса. function GetGlobalRoot() : AnsiString; //Вернуть корень. function Load(const FileName : AnsiString) : boolean; //Загрузить файл в класс. function Save(const FileName : AnsiString) : boolean; //Сохранить инфу из класса в файл. function ElementNameExists(const ElementName : AnsiString) : boolean; //Существует ли элемент с таким именем. function GetElementNumber(const ElementName : AnsiString) : Integer; //Какой номер у элемента с таким именем. function FileExistsHere(const FileName : AnsiString) : boolean; //Существует ли файл с таким именем. function GetFileNumberHere(const FileName : AnsiString) : Integer; //Какой номер у файла с таким именем. function DirExistsHere(const DirName : AnsiString) : boolean; //Существует ли дира с таким именем. function GetDirNumberHere(const DirName : AnsiString) : Integer; //Определить номер диры с таким именем. function GetRealElementName(const ElemName : AnsiString) : AnsiString; //Определить абсолютное имя для элемента function GetRealElementName(const ElemNum : Integer) : AnsiString; //Определить абсолютное имя для элемента с таким-то номером в списке. function AddFile(const FileName : AnsiString) : boolean; //Добавить файл. function AddRealFile(const FileName : AnsiString) : boolean; //Добавить реально существующий на диске файл (с подсчетом реальной инфы). function AddDirectory(const DirName : AnsiString) : boolean; //Добавить пустую диру. function AddRealDirectory(const DirName : AnsiString; const Recursive : boolean) : boolean; //Добавить реальную диру с подсчетом всей инфы по файлам. function RemoveFile(const FileName : AnsiString) : boolean; //Удалить файл из базы. function RemoveDirectory(const DirName : AnsiString) : boolean; //Удалить диру (со всеми входящими файлами и дирами). function CheckFile(const ListFileName, RealFileName : AnsiString) : byte; //Проверить насколько реальный файл соответствует инфе в файле. Возвращает значение одной из констант Flist_CheckFile_*. Можно сравнивать директории. procedure CompareDirs(const ListDirName, RealDirName : AnsiString; var ComparedList : AComparedList); //Сравнить 2 папки, результат записывается в ComparedList. function CompareWithFlist(var OtherFlist : TFlistFormat; const ListDirName : AnsiString; var ComparedList : AComparedList) : boolean; //Сравнить текущий файл с другим, считая его (этот другой файл) как будто за текущую версию файлов на венте. function CompareWithFlist(const OtherFlistFile, ListDirName : AnsiString; var ComparedList : AComparedList) : boolean; //Сравнить текущий файл с другим, считая его (этот другой файл) как будто за текущую версию файлов на венте. function ConvertToSimpleComparedList(var ComparedList : AComparedList) : Boolean; //Превратить список сравнения в нечно упрощенное - без лишней инфы про то как именно что различается, когда важен сам факт наличия различия. procedure ParseUsedGroups(const GroupsStr : AnsiString); //Пропарсить строку с группами и забить инфу в объект. function GenUsedGroups : AnsiString; //Используя инфу в объекте сгенерить строку с группами. procedure UpdateUsedGroups; //Просмотреть список и сгенерировать новую инфу о группах, записать её в объект. procedure TurnUseGroupsOn; //Включить режим с использованием групп. procedure TurnUseGroupsOff; //Выключить режим использования групп. end; //Функции в прямом, открытом виде %). function Flist_CheckFile_2Text(const Code : Byte) : AnsiString; //Сгенерить текстовую строку из кода Flist_CheckFile_* implementation ///////////////////////////////////////////// // TFlistFormat // ///////////////////////////////////////////// //-----------------------------------------// // Конструкторы-деструкторы... // //-----------------------------------------// constructor TFlistFormat.Create; begin //Создать вложенные объекты классов... UsedGroups := TStringList.Create; //Проставить дефолтные значения... Self.Clear(); ErrorMessage := 'none'; DoConsoleOutput := Flist_Def_DoConsoleOutput; end; destructor TFlistFormat.Destroy; begin //Выкидываем мусор SetLength(FileList,0); UsedGroups.Free; //Выполнить унаследованный деструктор inherited; end; //------------------------------------------// // Закрытые методы... // //------------------------------------------// function TFlistFormat.ParseFileType(const InputStr : AnsiString) : byte; //Вернуть код типа файла по строковому значению типа. begin if LowerCase(InputStr) = 'file' then begin //Файл Result := Flist_Filetype_File; end else if LowerCase(InputStr) = 'dir' then begin //Дира Result := Flist_Filetype_Directory; end else begin //хз-что Result := Flist_Filetype_Unknown; end; end; function TFlistFormat.ReturnFileTypeStr(const InputCode : byte) : AnsiString; //Наоборот сгенерить значение типа файла из кода. begin if InputCode = Flist_Filetype_File then begin //Файл Result := 'File'; end else if InputCode = Flist_Filetype_Directory then begin //Дира Result := 'Dir'; end else begin //хз-что Result := 'Unknown'; end; end; //------------------------------------------// // Открытые методы... // //------------------------------------------// function TFlistFormat.UseGroups : Boolean; //Используются ли группы файлов - возвращает значение, записать прямо его нельзя. begin Result := Self.ReallyUseGroups; end; procedure TFlistFormat.Clear(); //Очистить инфу класса. begin //Фактически - проставить дефолтные значения... PathsAreAbsolute := Flist_Def_PathsAreAbsolute; AddSize := Flist_Def_AddSize; AddCRC32 := Flist_Def_AddCRC32; FilesNum := 0; ReallyUseGroups := Flist_Def_UseGroups; Self.ParseUsedGroups(Flist_Def_UsedGroups); //Ну и массив в 0 элементов SetLength(FileList,0); //Другая служебная инфа. FileLoaded := false; LoadedFileName := ''; GlobalRoot := ''; end; function TFlistFormat.GetGlobalRoot() : AnsiString; //Вернуть корень. begin if Self.GlobalRoot = '' then begin //Если глобальный путь не указан. if Self.FileLoaded = true then begin //Если загружался какой-то файл - берем путь к этому файлу. Result := ExtractFilePath(Self.LoadedFileName); end else begin //Иначе берем путь к текущей дире. Result := GetCurrentDir(); end; end else begin //Если указан глобальный путь к корню - просто возвращаем его. Result := Self.GlobalRoot; end; end; function TFlistFormat.Load(const FileName : AnsiString) : boolean; //Загрузить инфу файла в класс. var AnIniFile : TAnIniFile; AllOk : boolean; IniValueList : AIniValueList; I, J, ReadVersion : Integer; begin //Инициализация. AnIniFile := TAnIniFile.Create; SetLength(IniValueList,0,0); //Инициализация пустого динамического массива для инфы секции [Files] Result := false; if Self.DoConsoleOutput = true then begin Writeln(_('Loading file ')+FileName+' ... '); end; //Очистка инфы класса... Self.Clear(); //Собсно грузим... AllOk := FileExists(FileName); if AllOk = false then begin //Если файла не существует... ErrorMessage := _('File ')+FileName+_(' is not exists.'); end else begin //Если сам файлик вообще существует... Загружаем... AllOk := AnIniFile.Load(FileName); if AllOk = false then begin //Не удалось загрузить файл. ErrorMessage := _('Error, loading file ')+FileName; end; end; if AllOk = true then begin //Ок, загружено. Читаем сигнатуру... if AnIniFile.ReadString('Main', 'FormatName') <> 'FileList' then begin //Это не FileList-файл. AllOk := false; ErrorMessage := _('File ')+FileName+_(' is not FileList file.'); end; end; if AllOk = true then begin //Проверяем версию формата... ReadVersion := AnIniFile.ReadInteger('Main', 'FormatVersion'); if ReadVersion > Flist_FormatVersion then begin //Формат более новой версии чем код в бинарнике. AllOk := false; 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.'); end else if ReadVersion < Flist_FormatVersion then begin //Загруженный файлик более старой версии - надо его обновить. if ReadVersion = 1 then begin // 1 -> 2. //[Main] AnIniFile.WriteBool('Main','UseGroups',Flist_Def_UseGroups); AnIniFile.WriteString('Main','UsedGroups',Flist_Def_UsedGroups); AnIniFile.WriteInteger('Main', 'FormatVersion',2); end; end; end; if AllOk = true then begin //Собсно читаем инфу... //[Main] PathsAreAbsolute := AnIniFile.ReadBool('Main','PathsAreAbsolute'); AddSize := AnIniFile.ReadBool('Main','AddSize'); AddCRC32 := AnIniFile.ReadBool('Main','AddCRC32'); //FilesNum := AnIniFile.ReadInteger('Main','FilesNum'); //Ненужно, важнее реальное количество в списке. ReallyUseGroups := AnIniFile.ReadBool('Main','UseGroups'); Self.ParseUsedGroups(AnIniFile.ReadString('Main','UsedGroups')); //[Files] AnIniFile.ReadIniValueList('Files',IniValueList); //Читаем секцию... //Заносим инфу о количестве файлов... FilesNum := Length(IniValueList); //Выделяем память под файлы... SetLength(FileList,FilesNum); //И собсно парсим... if FilesNum > 0 then begin //Если собсно есть что парсить. for I := 0 to FilesNum-1 do begin for J := 0 to Length(IniValueList[I])-1 do begin //По элементам. if IniValueList[I,J].Name = 'File' then begin FileList[I].FilePath := IniValueList[I,J].Value; end else if IniValueList[I,J].Name = 'Type' then begin FileList[I].FileType := ParseFileType(IniValueList[I,J].Value); end else if IniValueList[I,J].Name = 'Size' then begin FileList[I].FileSize := StrToInt(IniValueList[I,J].Value); end else if IniValueList[I,J].Name = 'CRC32' then begin FileList[I].CRC32 := StrToInt('$'+IniValueList[I,J].Value); //Знак доллара - т.к. читается число в виде HEX. end else if IniValueList[I,J].Name = 'Group' then begin FileList[I].Group := IniValueList[I,J].Value; end end; //Если используются группы - убедиться что файлу пробита группа. if Self.UseGroups = true then begin if Length(FileList[I].Group) = 0 then begin //Пусто - вбиваем "all" FileList[I].Group := 'all'; end; end; end; end; //Ну, вроде бы все что надо прочитано. Теперь отмечаем что фсеок и собсно все. Result := true; if Self.DoConsoleOutput = true then begin Writeln(_('Loaded file ')+FileName); end; FileLoaded := true; LoadedFileName := FileName; end; //Уборка мусора. AnIniFile.Free; SetLength(IniValueList,0,0); end; function TFlistFormat.Save(const FileName : AnsiString) : boolean; //Сохранить инфу из класса в файл. var AnIniFile : TAnIniFile; I, J, ValElemNum : Integer; IniValueList : AIniValueList; begin //Инициализация. AnIniFile := TAnIniFile.Create; SetLength(IniValueList,0,0); Result := false; if Self.DoConsoleOutput = true then begin Writeln(_('Saving file ')+FileName+' ... '); end; //Крейтим новое файло... AnIniFile.MakNewFile; //Закидываем инфу... //[Main] AnIniFile.WriteString('Main', 'FormatName', 'FileList'); AnIniFile.WriteInteger('Main', 'FormatVersion', Flist_FormatVersion); AnIniFile.WriteBool('Main', 'PathsAreAbsolute', PathsAreAbsolute); AnIniFile.WriteBool('Main', 'AddSize', AddSize); AnIniFile.WriteBool('Main', 'AddCRC32', AddCRC32); AnIniFile.WriteInteger('Main', 'FilesNum', FilesNum); AnIniFile.WriteBool('Main', 'UseGroups', UseGroups); AnIniFile.WriteString('Main', 'UsedGroups', GenUsedGroups); //[Files] //Определить сколько элементов будет в каждой "строке" IniValueList ValElemNum := 2; //Минимум. if AddSize = true then ValElemNum := ValElemNum+1; if AddCRC32 = true then ValElemNum := ValElemNum+1; if UseGroups = true then ValElemNum := ValElemNum+1; //Выделить память под элементы IniValueList SetLength(IniValueList,FilesNum,ValElemNum); //Закинуть всю инфу по файлам в IniValueList for I := 0 to FilesNum-1 do begin J := 0; //File IniValueList[I,J].Name := 'File'; IniValueList[I,J].Value := FileList[I].FilePath; J := J+1; //Type IniValueList[I,J].Name := 'Type'; IniValueList[I,J].Value := ReturnFileTypeStr(FileList[I].FileType); J := J+1; //Size if AddSize = true then begin IniValueList[I,J].Name := 'Size'; IniValueList[I,J].Value := IntToStr(FileList[I].FileSize); J := J+1; end; //CRC32 if AddCRC32 = true then begin IniValueList[I,J].Name := 'CRC32'; IniValueList[I,J].Value := IntToHex(FileList[I].CRC32,8); end; //Group if UseGroups = true then begin IniValueList[I,J].Name := 'Group'; IniValueList[I,J].Value := FileList[I].Group; end; end; //Записать сгенеренный список в секцию... AnIniFile.WriteIniValueList('Files',IniValueList); //Терь можно сохранить файл... Result := AnIniFile.Save(FileName); //Если сохранилось удачно - запоминаем чего там за файлег. if Result = true then begin FileLoaded := true; LoadedFileName := FileName; if Self.DoConsoleOutput = true then begin Writeln(_('Saved file ')+FileName); end; end; //Выкидываем хлам (ака мусор). AnIniFile.Free; SetLength(IniValueList,0,0); end; function TFlistFormat.ElementNameExists(const ElementName : AnsiString) : boolean; //Существует ли элемент с таким именем. var I : Integer; TempElemName : AnsiString; begin //Инициализация. Result := false; I := -1; TempElemName := UpperCase(DelLastSlash(ElementName)); //Пробежаться по массиву с файлами пока элемент не будет найден или пока массив не закончится. repeat I := I+1; //Итак, независимо от того что это - папка ли, файл ли - смотрим. if UpperCase(DelLastSlash(FileList[I].FilePath)) = TempElemName then begin Result := true; //Все, нашли, из цикла оно терь вылетит и вернется true. end; until (Result = true) or (I = FilesNum-1); end; function TFlistFormat.GetElementNumber(const ElementName : AnsiString) : Integer; //Какой номер у элемента с таким именем. var I : Integer; TempElemName : AnsiString; begin //Инициализация. Result := -1; I := -1; TempElemName := UpperCase(DelLastSlash(ElementName)); //Пробежаться по массиву с файлами пока элемент не будет найден или пока массив не закончится. repeat I := I+1; //Итак, независимо от того что это - папка ли, файл ли - смотрим. if UpperCase(DelLastSlash(FileList[I].FilePath)) = TempElemName then begin Result := I; //Все, нашли, из цикла оно терь вылетит и вернется true. end; until (Result = I) or (I = FilesNum-1); end; function TFlistFormat.FileExistsHere(const FileName : AnsiString) : boolean; //Существует ли файл с таким именем. var I : Integer; TempFileName : AnsiString; begin //Инициализация. Result := false; I := -1; TempFileName := UpperCase(FileName); //Пробежаться по массиву с файлами пока элемент не будет найден или пока массив не закончится. repeat I := I+1; if FileList[I].FileType = Flist_Filetype_File then begin //Только если это файло. if UpperCase(FileList[I].FilePath) = TempFileName then begin Result := true; //Все, нашли, из цикла оно терь вылетит и вернется true. end; end; until (Result = true) or (I = FilesNum-1); end; function TFlistFormat.GetFileNumberHere(const FileName : AnsiString) : Integer; //Какой номер у файла с таким именем. var I : Integer; TempFileName : AnsiString; begin //Инициализация. Result := -1; I := -1; TempFileName := UpperCase(FileName); //Пробежаться по массиву с файлами пока элемент не будет найден или пока массив не закончится. repeat I := I+1; if FileList[I].FileType = Flist_Filetype_File then begin //Только если это файло. if UpperCase(FileList[I].FilePath) = TempFileName then begin Result := I; //Все, нашли, из цикла оно терь вылетит и вернется true. end; end; until (Result = I) or (I = FilesNum-1); end; function TFlistFormat.DirExistsHere(const DirName : AnsiString) : boolean; //Существует ли дира с таким именем. var I : Integer; TempDirName : AnsiString; begin //Инициализация. Result := false; I := -1; TempDirName := DelLastSlash(UpperCase(DirName)); //Пробежаться по массиву с файлами пока элемент не будет найден или пока массив не закончится. repeat I := I+1; if FileList[I].FileType = Flist_Filetype_Directory then begin //Только если это дира. if UpperCase(DelLastSlash(FileList[I].FilePath)) = TempDirName then begin Result := true; //Все, нашли, из цикла оно терь вылетит и вернется true. end; end; until (Result = true) or (I = FilesNum-1); end; function TFlistFormat.GetDirNumberHere(const DirName : AnsiString) : Integer; //Определить номер диры с таким именем. var I : Integer; TempDirName : AnsiString; begin //Инициализация. Result := -1; I := -1; TempDirName := DelLastSlash(UpperCase(DirName)); //Пробежаться по массиву с файлами пока элемент не будет найден или пока массив не закончится. repeat I := I+1; if FileList[I].FileType = Flist_Filetype_Directory then begin //Только если это дира. if UpperCase(DelLastSlash(FileList[I].FilePath)) = TempDirName then begin Result := I; //Все, нашли, из цикла оно терь вылетит и вернется true. end; end; until (Result = I) or (I = FilesNum-1); end; function TFlistFormat.GetRealElementName(const ElemName : AnsiString) : AnsiString; //Определить абсолютное имя для элемента begin //Получаем абсолютный путь.... if IsLocalPath(ElemName) = true then begin //Если надо генерить - генерим. Result := ResolveLocalPath(ElemName,Self.GetGlobalRoot()); end else begin //Он уже глобальный. Result := ElemName; end; end; function TFlistFormat.GetRealElementName(const ElemNum : Integer) : AnsiString; //Определить абсолютное имя для элемента с таким-то номером в списке. begin //Просто вызываем функцию выше с именем элемента... Result := GetRealElementName(Self.FileList[ElemNum].FilePath); end; function TFlistFormat.AddFile(const FileName : AnsiString) : boolean; //Добавить файл. var CurrFileNum : Integer; LocalName, GlobalName : AnsiString; begin //Инициализация. Result := false; CurrFileNum := FilesNum; //После добавления нового файла эта цифра будет номером последнего элемента массива. //Увеличить размер массива на один. FilesNum := FilesNum+1; SetLength(FileList,FilesNum); //Получаем абсолютный путь.... if IsLocalPath(FileName) = true then begin //Если надо генерить - генерим. GlobalName := ResolveLocalPath(FileName,Self.GetGlobalRoot()); end else begin //Он уже глобальный. GlobalName := FileName; end; //Если нужно - получаем относительный путь. if Self.PathsAreAbsolute = false then begin LocalName := GetLocalPath(Self.GetGlobalRoot(),GlobalName); end; //Забить параметры для файла. //FilePath if Self.PathsAreAbsolute = true then begin //Имя должно быть абсолютное. FileList[CurrFileNum].FilePath := GlobalName; end else begin //Имя должно быть относительное FileList[CurrFileNum].FilePath := LocalName; end; //FileType FileList[CurrFileNum].FileType := Flist_Filetype_File; //FileSize if Self.AddSize = true then begin FileList[CurrFileNum].FileSize := 0; end; //CRC32 if Self.AddCRC32 = true then begin FileList[CurrFileNum].CRC32 := crc32(0, nil, 0); //Получится "пустое" значение CRC32. end; //Вернуть результат. Result := true; end; function TFlistFormat.AddRealFile(const FileName : AnsiString) : boolean; //Добавить реально существующий на диске файл (с подсчетом реальной инфы). var CurrFileNum : Integer; LocalName, GlobalName : AnsiString; begin //Код независит от функции TFlistFormat.AddFile в целях более оптимальной работы. //Инициализация. Result := false; CurrFileNum := FilesNum; //После добавления нового файла эта цифра будет номером последнего элемента массива. //Получаем абсолютный путь.... if IsLocalPath(FileName) = true then begin //Если надо генерить - генерим. GlobalName := ResolveLocalPath(FileName,Self.GetGlobalRoot()); end else begin //Он уже глобальный. GlobalName := FileName; end; //Если нужно - получаем относительный путь. if Self.PathsAreAbsolute = false then begin LocalName := GetLocalPath(Self.GetGlobalRoot(),GlobalName); end; //Увеличить размер массива на один. FilesNum := FilesNum+1; SetLength(FileList,FilesNum); //Забить параметры для файла. //FilePath if Self.PathsAreAbsolute = true then begin //Имя должно быть абсолютное. FileList[CurrFileNum].FilePath := GlobalName; end else begin //Имя должно быть относительное FileList[CurrFileNum].FilePath := LocalName; end; //FileType FileList[CurrFileNum].FileType := Flist_Filetype_File; //FileSize if Self.AddSize = true then begin FileList[CurrFileNum].FileSize := GetFileSize(GlobalName); //Подсчитать размер. Если файла нету - вернет нолик. end; //CRC32 if Self.AddCRC32 = true then begin FileList[CurrFileNum].CRC32 := FileToCRC32(GlobalName,DoConsoleOutput); //Подсчитать CRC32 для файла. Если файла нету - будет "пустое" значение. end; //Вернуть результат. Result := true; end; function TFlistFormat.AddDirectory(const DirName : AnsiString) : boolean; //Добавить пустую диру. var CurrFileNum : Integer; LocalName, GlobalName : AnsiString; begin //Инициализация. Result := false; CurrFileNum := FilesNum; //После добавления новой диры эта цифра будет номером последнего элемента массива. //Увеличить размер массива на один. FilesNum := FilesNum+1; SetLength(FileList,FilesNum); //Получаем абсолютный путь.... if IsLocalPath(DirName) = true then begin //Если надо генерить - генерим. GlobalName := DelLastSlash(ResolveLocalPath(DirName,Self.GetGlobalRoot())); end else begin //Он уже глобальный. GlobalName := DelLastSlash(DirName); end; //Если нужно - получаем относительный путь. if Self.PathsAreAbsolute = false then begin LocalName := GetLocalPath(Self.GetGlobalRoot(),GlobalName); end; //Забить параметры для диры. //FilePath if Self.PathsAreAbsolute = true then begin //Имя должно быть абсолютное. FileList[CurrFileNum].FilePath := GlobalName; end else begin //Имя должно быть относительное FileList[CurrFileNum].FilePath := LocalName; end; //FileType FileList[CurrFileNum].FileType := Flist_Filetype_Directory; //Вернуть результат. Result := true; end; function TFlistFormat.AddRealDirectory(const DirName : AnsiString; const Recursive : boolean) : boolean; //Добавить реальную диру с подсчетом всей инфы по файлам. var AddingFiles : LongInt; LocalName, GlobalName : AnsiString; function ReallyAddDirectory(const DirName : AnsiString; const Recursive : boolean) : boolean; //Функция, непосредственно читающая инфу по директории... var SearchRec : TSearchRec; LocalDirName : AnsiString; begin //Инициализация. Result := true; //Если нужно - получаем относительный путь. if Self.PathsAreAbsolute = false then begin LocalDirName := GetLocalPath(Self.GetGlobalRoot(),DirName); end; //Начать сбор инфы... if FindFirst(AddLastSlash(DirName)+'*', faAnyFile and faDirectory, SearchRec) = 0 then begin //Если поиск успешно начат. repeat //Повторяем поиск пока не закончится то что можно искать %). if (SearchRec.Name <> '.') and (SearchRec.Name <> '..') then begin //Чего-то нашли. if (SearchRec.Attr and faDirectory) <> faDirectory then begin //Ход мыслей прост. Если то что мы нашли не директория - значит это файл %). Ибо проверять //по принципу "файл ли это" опасно - директория - она тоже файл %). //Итак, то что нашли - файл. Добавляем. //Забить параметры для файла. //FilePath if Self.PathsAreAbsolute = true then begin //Имя должно быть абсолютное. FileList[FilesNum].FilePath := AddLastSlash(DirName)+SearchRec.Name; end else begin //Имя должно быть относительное FileList[FilesNum].FilePath := AddLastSlash(LocalDirName)+SearchRec.Name; end; //FileType FileList[FilesNum].FileType := Flist_Filetype_File; //FileSize if Self.AddSize = true then begin try FileList[FilesNum].FileSize := GetFileSize(AddLastSlash(DirName)+SearchRec.Name); //Подсчитать размер. Если файла нету - вернет нолик. except Self.ErrorMessage := _('Can''t calc file size for ')+AddLastSlash(DirName)+SearchRec.Name; Result := false; Break; end; end; //CRC32 if Self.AddCRC32 = true then begin try FileList[FilesNum].CRC32 := FileToCRC32(AddLastSlash(DirName)+SearchRec.Name,DoConsoleOutput); //Подсчитать CRC32 для файла. Если файла нету - будет "пустое" значение. except Self.ErrorMessage := _('Can''t calc CRC32 for ')+AddLastSlash(DirName)+SearchRec.Name; Result := false; Break; end; end; //+1 счетчик файлов в массиве с инфой. FilesNum := FilesNum+1; end else begin //Если то что нашли - директория. Аналогично - добавляем... //Забить параметры для диры. //FilePath if Self.PathsAreAbsolute = true then begin //Имя должно быть абсолютное. FileList[FilesNum].FilePath := AddLastSlash(DirName)+SearchRec.Name; end else begin //Имя должно быть относительное FileList[FilesNum].FilePath := AddLastSlash(LocalDirName)+SearchRec.Name; end; //FileType FileList[FilesNum].FileType := Flist_Filetype_Directory; //+1 счетчик файлов в массиве с инфой. FilesNum := FilesNum+1; //И если нужно - вызвать функцию рекурсивно. if Recursive = true then begin Result := ReallyAddDirectory(AddLastSlash(DirName)+SearchRec.Name, Recursive); if Result = false then begin //Был какой-то ошибк. Брякаем. Break; end; end; end; end; until FindNext(SearchRec) <> 0; //Закрыть поиск. SysUtils.FindClose(SearchRec); end; end; begin //Функция может работать "рекурсивно". На самом деле прямой рекурсии этой функции даже в этом случае //не будет - рекурситься будут только "вложенные" функции. В любом случае - сначала подсчитывает //количество добавляемых файлов (либо рекурсивно либо нет, смотря что требуется), потом выделяется память //под массив, потом вызывается функция добавления данной папки, которая уже может быть рекурсивной если //это необходимо. Таким образом размер динамического массива определяется однократно, что позволяет не //заниматься копированием памяти каждый раз когда надо увеличить массив. Рекурсивная функция уже точно знает //сколько файлов будет добавлено и размер массива не трогает - только читает и добавляет инфу. //Инициализация. Result := false; //Получаем абсолютный путь.... if IsLocalPath(DirName) = true then begin //Если надо генерить - генерим. GlobalName := DelLastSlash(ResolveLocalPath(DirName,Self.GetGlobalRoot())); end else begin //Он уже глобальный. GlobalName := DelLastSlash(DirName); end; //Если нужно - получаем относительный путь. if Self.PathsAreAbsolute = false then begin LocalName := GetLocalPath(Self.GetGlobalRoot(),GlobalName); end; //Итак, во-первых подсчитать количество добавляемых файлов... AddingFiles := GetFilesNumber(GlobalName,Recursive)+1; //+1 - данная конкретная дира которая DirName //Теперь выставить правильный размер массива... FilesNum не трогаем - это в принципе счетчик тут. SetLength(FileList,FilesNum+AddingFiles); //Добавить текущую диру. //FilePath if Self.PathsAreAbsolute = true then begin //Имя должно быть абсолютное. FileList[FilesNum].FilePath := GlobalName; end else begin //Имя должно быть относительное FileList[FilesNum].FilePath := LocalName; end; //FileType FileList[FilesNum].FileType := Flist_Filetype_Directory; //+1 количество файлов в инфе. FilesNum := FilesNum+1; //Ок. Можно звать функцию для непосредственного чтения инфы директории, если надо то она и будет рекурситься. Result := ReallyAddDirectory(GlobalName, Recursive); end; function TFlistFormat.RemoveFile(const FileName : AnsiString) : boolean; //Удалить файл из базы. var RealSearchPath, TempStr : AnsiString; Finded : boolean; I, J : Integer; NewFileList : array of RFlistFile; DeletingElem, DummyElem : RFlistFile; begin //Инициализация Result := false; //Смотрим что там за путь в аргументе и если надо приводим его к тому виду как они в массиве. if Self.PathsAreAbsolute = true then begin //Если нужны абсолютные пути. if IsLocalPath(FileName) = true then begin //Если надо генерить - генерим. RealSearchPath := ResolveLocalPath(FileName,Self.GetGlobalRoot()); end else begin //Он уже глобальный. RealSearchPath := FileName; end; end else begin //Если нужны относительные пути. if IsLocalPath(FileName) = true then begin //Он уже локальный. RealSearchPath := FileName; end else begin //Генерим локальный RealSearchPath := GetLocalPath(Self.GetGlobalRoot(),FileName); end; end; //Ищем файл с таким локальным\глобальным именем и удаляем его из массива. Finded := false; I := -1; repeat I := I+1; if UpperCase(Self.FileList[I].FilePath) = UpperCase(RealSearchPath) then begin //Нашли. Finded := true; end; until (Finded = true) or (I = FilesNum-1); if Finded = true then begin //Если нашли чего удалять, а удаляемый элемент не последний - двигаем элементы внутрях массива. if I <> (FilesNum-1) then begin Move(Self.FileList[I],DeletingElem,SizeOf(RFlistFile)); //Временно копируем инфу удаляемого элемента. Move(Self.FileList[I+1],Self.FileList[I],SizeOf(RFlistFile)*(FilesNum-(I+1))); //Переносим инфу других элементов. Move(DeletingElem,Self.FileList[FilesNum-1],SizeOf(RFlistFile)); //Копируем удаляемый элемент в конец массива чтоб его прочистило при смене размера массива (во избежание утечков памяти). Move(DummyElem,DeletingElem,SizeOf(RFlistFile)); //Копируем во временный элемент инфу элемента заглушки - чтобы тупой сборщег мусора не полез по ссылкам и не заховал последний элемент массива раньше или позже (т.е. еще раз на пустое место) правильного времени. end; //Прочистить удаляемый элемент массива. Self.FileList[FilesNum-1].CRC32 := 0; Self.FileList[FilesNum-1].FilePath := ''; Self.FileList[FilesNum-1].FileSize := 0; Self.FileList[FilesNum-1].FileType := 0; //И почистить массив. FilesNum := FilesNum-1; SetLength(Self.FileList,FilesNum); //Done. Result := true; end; end; function TFlistFormat.RemoveDirectory(const DirName : AnsiString) : boolean; //Удалить диру (со всеми входящими файлами и дирами). var I : Integer; RealSearchPath : AnsiString; begin //Принцип действия следующий: двигаемся по массиву, если обнаруживаем child-а (в т.ч. и сама эта дира //попадет в этот фильтр - явно её удалять и не надо) - то удаляем вышенаписанной функцией, счетчик //отщелкиваем назад и идем дальше. //Инициализация. Result := false; //Смотрим что там за путь в аргументе и если надо приводим его к тому виду как они в массиве. if Self.PathsAreAbsolute = true then begin //Если нужны абсолютные пути. if IsLocalPath(DirName) = true then begin //Если надо генерить - генерим. RealSearchPath := ResolveLocalPath(DirName,Self.GetGlobalRoot()); end else begin //Он уже глобальный. RealSearchPath := DirName; end; end else begin //Если нужны относительные пути. if IsLocalPath(DirName) = true then begin //Он уже локальный. RealSearchPath := DirName; end else begin //Генерим локальный RealSearchPath := GetLocalPath(Self.GetGlobalRoot(),DirName); end; end; //Шканим массиву пока счетчик не дойдет до конца массива... I := -1; //Начинаем с нуля. repeat I := I+1; //Счетчик. if I < Self.FilesNum then begin //Эта проверка счетчика на всякий пожарный, ибо мало ли, мы таки будем в процессе работы //изменять размер массива который обсчитывается этим самым FilesNum... if IsDirsChild(RealSearchPath, Self.FileList[I].FilePath) = true then begin //Найден элемент который надо удалять. Удаляем. RemoveFile(Self.FileList[I].FilePath); //И отщелкиваем назад счетчик. I := I-1; end; end; until I = Self.FilesNum-1; end; function TFlistFormat.CheckFile(const ListFileName, RealFileName : AnsiString) : byte; //Проверить насколько реальный файл соответствует инфе в файле. //Возвращает значение одной из констант Flist_CheckFile_*. Можно сравнивать директории. var IsDirectory : boolean; CurElemNum : Integer; begin //Инициализация. Result := Flist_CheckFile_Error; //По умолчанию возвращается код ошибки... //С начала надо узнать с чем имеем дело. Папка или файл. //Проверяем, существует ли файл на диске... if FileExists(RealFileName) = true then begin //То с чем работаем - файл. IsDirectory := false; end else if DirectoryExists(DelLastSlash(RealFileName)) = true then begin //То с чем работаем - директория. IsDirectory := true; end else begin //Нифига на диске нету, ни файла, ни директории. Проверяем - есть ли чего в базе. if ElementNameExists(RealFileName) = true then begin //Файло существует в базе, но не существует на диске. Что именно оно из себя //представляет - директорию или файл - уже непринципиально. Result := Flist_CheckFile_FileNotExists; end else begin //Файло не существует ни в базе, ни на диске - т.е. нигде. Result := Flist_CheckFile_NothingExists; end; end; if Result = Flist_CheckFile_Error then begin //а Flist_CheckFile_Error это еще и индикатор того что функция пока не нашла ответ % )). //И то что выполнение попало сюда - означает, что как минимум искомое есть на винте. //В зависимости от того - папка это или файл - ищем нужный элемент в базе... if IsDirectory = false then begin //Ищем файл. CurElemNum := GetFileNumberHere(ListFileName); if CurElemNum >= 0 then begin //Файл в базе существует. //Если нужно, сверяем размер файла... if Self.AddSize = true then begin if GetFileSize(RealFileName) <> Self.FileList[CurElemNum].FileSize then begin //Не совпало. Сообщаем. Result := Flist_CheckFile_MismatchedSize; end; end; //Если еще нет ответа - ищем дальше... if Result = Flist_CheckFile_Error then begin //Если нужно - сверяем CRC32. if Self.AddCRC32 = true then begin if FileToCRC32(RealFileName) <> Self.FileList[CurElemNum].CRC32 then begin //Не совпало. Сообщаем. Result := Flist_CheckFile_MismatchedCRC32; end; end; end; //Если до сих пор нет никакого ответа - файл считаем идентичным. if Result = Flist_CheckFile_Error then begin Result := Flist_CheckFile_Identical; end; end else begin //Нету такого файла, о чем и сообщаем. Result := Flist_CheckFile_ElementNotExists; end; end; end else begin //Ищем директорию. CurElemNum := GetDirNumberHere(ListFileName); if CurElemNum >= 0 then begin //Директория в базе существует. Просто отмечаем этот факт считая что там все идентично. Ибо у папки нету ни размера ни CRC32. Result := Flist_CheckFile_ElementNotExists; end; end; end; procedure TFlistFormat.CompareDirs(const ListDirName, RealDirName : AnsiString; var ComparedList : AComparedList); //Сравнить 2 папки, результат записывается в ComparedList. var ListRealSize, I : Integer; AbsoluteName, ListName : AnsiString; MismatchFound : Boolean; procedure ReserveComparedListMem(); //Если необходимо - "зарезервировать" дополнительное место в массиве ComparedList begin if ListRealSize >= Length(ComparedList) then begin //Выделить еще кусок памяти если нужно. SetLength(ComparedList, Length(ComparedList)+50); end; end; function ElementReported(const ElemRepName : AnsiString) : Boolean; //Проверить - есть ли уже инфа об этом элементе в отчете. var I : Integer; begin Result := false; //Ишем... for I := 0 to ListRealSize-1 do begin if UpperCase(ComparedList[I].FilePath) = UpperCase(ElemRepName) then begin //Нашли. Result := true; Exit; end; end; end; procedure RecursiveCompareDir(const ListName, RealName : AnsiString); //Собсно рекурсивная функция для сверки директории с инфой Flist. var SearchRec : TSearchRec; SearchElemNum : Integer; TmpCheckResult : Byte; begin //Для начала - надо проверить соответствие самой директории. Смотрим, нет ли уже по ней инфы в отчете... if ElementReported(ListName) = false then begin //Есть ли директория в базе. if DirExistsHere(ListName) = false then begin //Нема - сообщаем об этом в отчОт. ReserveComparedListMem(); ComparedList[ListRealSize].FilePath := FileList[I].FilePath; ComparedList[ListRealSize].FileType := Flist_Filetype_Directory; ComparedList[ListRealSize].CheckResult := Flist_CheckFile_ElementNotExists; ListRealSize := ListRealSize+1; end; end; if FindFirst(AddLastSlash(RealName)+'*', faAnyFile and faDirectory, SearchRec) = 0 then begin //Если поиск начат успешно - начинаем рекурсивно смотреть содержимое. repeat if (SearchRec.Name <> '.') and (SearchRec.Name <> '..') then begin //Чего-то нашли. Смотрим чего там такое. if (SearchRec.Attr and faDirectory) = faDirectory then begin //Это директория. Вызываем рекурсию - проверит директорию оно само.. RecursiveCompareDir(AddLastSlash(ListName)+SearchRec.Name,AddLastSlash(RealName)+SearchRec.Name); end else begin //Это файл. Смотрим, есть ли такой файл в отчете... if ElementReported(AddLastSlash(ListName)+SearchRec.Name) = false then begin //Про элемент нету инфы в отчете. Посылаем его на полную проверку % ))). TmpCheckResult := Self.CheckFile(AddLastSlash(ListName)+SearchRec.Name,AddLastSlash(RealName)+SearchRec.Name); if TmpCheckResult <> Flist_CheckFile_Identical then begin //В результате проверки было обнаружено расхождение, которое необходимо зафиксировать в отчете. ReserveComparedListMem(); ComparedList[ListRealSize].FilePath := AddLastSlash(ListName)+SearchRec.Name; ComparedList[ListRealSize].FileType := Flist_Filetype_File; ComparedList[ListRealSize].CheckResult := TmpCheckResult; ListRealSize := ListRealSize+1; end; end; end; end; until FindNext(SearchRec) <> 0; //Если поиск завершен - закрываем его. SysUtils.FindClose(SearchRec); end; end; begin //Сначала пробежать по массиву с инфой, сравнить с реальной инфой на венте, зафиксировать найденное в результирующем списке. //Потом пробежаться по реальной дире - для каждого найденного элемента - проверить, если инфы в результирующем списке //нема - проверять большой список. //Занулить ComparedList. SetLength(ComparedList,0); //Задать начальный размер ComparedList. ListRealSize := 0; //Сколько на самом деле элементов в массиве. SetLength(ComparedList,Length(Self.FileList)+50); //Типо столько памяти для начала, вполне возможно что хватит и изменять размер не придется. //Пробежаться по базе с файлами... for I := 0 to FilesNum-1 do begin //Индикатор найденного расхождения. MismatchFound := false; //Проверяем - относится ли искомое к кругу поиска... if IsDirsChild(ListDirName,Self.FileList[I].FilePath) = true then begin //Относится. Получаем абсолютное имя элемента... if Self.PathsAreAbsolute = true then begin //Используются абсолютные пути... Значит просто берется абсолютное имя файла из базы... AbsoluteName := Self.FileList[I].FilePath; end else begin //Используются относительные пути. Получаем путь относительно ListDirName, потом абсолютный относительно RealDirName AbsoluteName := ResolveLocalPath(GetLocalPath(ListDirName, FileList[I].FilePath), RealDirName); end; //Смотрим чего за элемент - файл или директория? if Self.FileList[I].FileType = Flist_Filetype_File then begin //Файло. Смотрим, существует ли такое файло на венте. if FileExists(AbsoluteName) = true then begin //Файло на венте существует. Если нужно - сверяем размер... if Self.AddSize = true then begin if GetFileSize(AbsoluteName) <> FileList[I].FileSize then begin //Несовпадение по размеру. Сообщаем. ReserveComparedListMem(); ComparedList[ListRealSize].FilePath := FileList[I].FilePath; ComparedList[ListRealSize].FileType := Flist_Filetype_File; ComparedList[ListRealSize].CheckResult := Flist_CheckFile_MismatchedSize; ListRealSize := ListRealSize+1; MismatchFound := true; end; end; //Если нужно - сверяем CRC32. if (Self.AddCRC32 = true) and (MismatchFound = false) then begin if FileToCRC32(AbsoluteName) <> FileList[I].CRC32 then begin //Несовпадение по CRC32. Сообщаем. ReserveComparedListMem(); ComparedList[ListRealSize].FilePath := FileList[I].FilePath; ComparedList[ListRealSize].FileType := Flist_Filetype_File; ComparedList[ListRealSize].CheckResult := Flist_CheckFile_MismatchedCRC32; ListRealSize := ListRealSize+1; MismatchFound := true; end; end; end else begin //Объект есть в базе, но его нету на винте. Создаем элемент отчета, закидываем инфу. ReserveComparedListMem(); ComparedList[ListRealSize].FilePath := FileList[I].FilePath; ComparedList[ListRealSize].FileType := Flist_Filetype_File; ComparedList[ListRealSize].CheckResult := Flist_CheckFile_FileNotExists; ListRealSize := ListRealSize+1; MismatchFound := true; end; end else if Self.FileList[I].FileType = Flist_Filetype_Directory then begin //Директория. Смотрим, существует ли такая директория на венте. if DirectoryExists(AbsoluteName) = false then begin //Объект есть в базе, но его нету на винте. Создаем элемент отчета, закидываем инфу. ReserveComparedListMem(); ComparedList[ListRealSize].FilePath := FileList[I].FilePath; ComparedList[ListRealSize].FileType := Flist_Filetype_Directory; ComparedList[ListRealSize].CheckResult := Flist_CheckFile_FileNotExists; ListRealSize := ListRealSize+1; MismatchFound := true; end; end; end; end; //Ну, типо пробежались. Теперь надо пробежаться по реальной директории, если она сама вообще есть, естественно. //Для этого желательно воспользоваться рекурсивной функцией, что щаз и будет зделано. RecursiveCompareDir(ListDirName, RealDirName); //Ок, по идее отчет уже сформирован. Теперь надо "устаканить" размер массива-отчета. SetLength(ComparedList,ListRealSize); end; function TFlistFormat.CompareWithFlist(var OtherFlist : TFlistFormat; const ListDirName : AnsiString; var ComparedList : AComparedList) : boolean; //Сравнить текущий файл с другим, считая его (этот другой файл) как будто за текущую версию файлов на венте. var ListRealSize, I, SearchElemNum : Integer; MismatchFound : Boolean; procedure ReserveComparedListMem(); //Если необходимо - "зарезервировать" дополнительное место в массиве ComparedList begin if ListRealSize >= Length(ComparedList) then begin //Выделить еще кусок памяти если нужно. SetLength(ComparedList, Length(ComparedList)+50); end; end; function ElementReported(const ElemRepName : AnsiString) : Boolean; //Проверить - есть ли уже инфа об этом элементе в отчете. var I : Integer; begin Result := false; //Ишем... for I := 0 to ListRealSize-1 do begin if UpperCase(ComparedList[I].FilePath) = UpperCase(ElemRepName) then begin //Нашли. Result := true; Exit; end; end; end; begin //Инициализация. Result := false; //Для начала занулить ComparedList. SetLength(ComparedList,0); //Задать начальный размер ComparedList. ListRealSize := 0; //Сколько на самом деле элементов в массиве. SetLength(ComparedList,Length(Self.FileList)+50); //Типо столько памяти для начала, вполне возможно что хватит и изменять размер не придется. //Пробежимся по данному файлу, сравнивая инфу с "другим" файлом... for I := 0 to FilesNum-1 do begin //Индикатор найденности несоответствия. MismatchFound := false; //Проверяем - относится ли искомое к кругу поиска... if IsDirsChild(ListDirName,Self.FileList[I].FilePath) = true then begin //В зависимости от того, дира это или файло... if FileList[I].FileType = Flist_Filetype_Directory then begin //Директория. Сверяем. if OtherFlist.DirExistsHere(FileList[I].FilePath) = false then begin //Нема такой диры в "текущем" файле. Оставляем отчет. ReserveComparedListMem(); ComparedList[ListRealSize].FilePath := FileList[I].FilePath; ComparedList[ListRealSize].FileType := Flist_Filetype_Directory; ComparedList[ListRealSize].CheckResult := Flist_CheckFile_FileNotExists; ListRealSize := ListRealSize+1; MismatchFound := true; end; end else begin //Файло. Сверяем. SearchElemNum := OtherFlist.GetFileNumberHere(FileList[I].FilePath); if SearchElemNum >= 0 then begin //Существует файлег. Если есть возможность - сверяем размеры. if (Self.AddSize = true) and (OtherFlist.AddSize = true) then begin if Self.FileList[I].FileSize <> OtherFlist.FileList[SearchElemNum].FileSize then begin //Не совпал размер. Оставляем отчет. ReserveComparedListMem(); ComparedList[ListRealSize].FilePath := FileList[I].FilePath; ComparedList[ListRealSize].FileType := Flist_Filetype_File; ComparedList[ListRealSize].CheckResult := Flist_CheckFile_MismatchedSize; ListRealSize := ListRealSize+1; MismatchFound := true; end; end; //Если есть необходимость и возможность - сверяем CRC32. if (MismatchFound = false) and (Self.AddCRC32 = true) and (OtherFlist.AddCRC32 = true) then begin if Self.FileList[I].CRC32 <> OtherFlist.FileList[SearchElemNum].CRC32 then begin //Не совпала чексумма. Оставляем отчет. ReserveComparedListMem(); ComparedList[ListRealSize].FilePath := FileList[I].FilePath; ComparedList[ListRealSize].FileType := Flist_Filetype_File; ComparedList[ListRealSize].CheckResult := Flist_CheckFile_MismatchedCRC32; ListRealSize := ListRealSize+1; MismatchFound := true; end; end; end else begin //Нема такого файла. Оставляем отчет. ReserveComparedListMem(); ComparedList[ListRealSize].FilePath := FileList[I].FilePath; ComparedList[ListRealSize].FileType := Flist_Filetype_File; ComparedList[ListRealSize].CheckResult := Flist_CheckFile_FileNotExists; ListRealSize := ListRealSize+1; MismatchFound := true; end; end; end; end; //Ок, пробежались. Теперь надо пробежаться по "другому" файлу, сравнивая с данным и отметить все, чего "не хватает". for I := 0 to OtherFlist.FilesNum-1 do begin //Проверяем - относится ли искомое к кругу поиска... if IsDirsChild(ListDirName,OtherFlist.FileList[I].FilePath) = true then begin if ElementReported(OtherFlist.FileList[I].FilePath) = false then begin //Если о элементе пока нифига в отчете, то смотрим базу в зависимости от того, дира это или файло... if OtherFlist.FileList[I].FileType = Flist_Filetype_Directory then begin //Директория. Сверяем. if Self.DirExistsHere(OtherFlist.FileList[I].FilePath) = false then begin //Нема. Сообщаем. ReserveComparedListMem(); ComparedList[ListRealSize].FilePath := OtherFlist.FileList[I].FilePath; ComparedList[ListRealSize].FileType := Flist_Filetype_Directory; ComparedList[ListRealSize].CheckResult := Flist_CheckFile_ElementNotExists; ListRealSize := ListRealSize+1; end; end else begin //Файло. Сверяем. if Self.FileExistsHere(OtherFlist.FileList[I].FilePath) = false then begin //Нема. Сообщаем. ReserveComparedListMem(); ComparedList[ListRealSize].FilePath := OtherFlist.FileList[I].FilePath; ComparedList[ListRealSize].FileType := Flist_Filetype_File; ComparedList[ListRealSize].CheckResult := Flist_CheckFile_ElementNotExists; ListRealSize := ListRealSize+1; end; end; end; end; end; //Ок, по идее отчет уже сформирован. Теперь надо "устаканить" размер массива-отчета. SetLength(ComparedList,ListRealSize); //И вернуть результат. Result := true; end; function TFlistFormat.CompareWithFlist(const OtherFlistFile, ListDirName : AnsiString; var ComparedList : AComparedList) : boolean; //Сравнить текущий файл с другим, считая его (этот другой файл) как будто за текущую версию файлов на венте. var OtherFlist : TFlistFormat; begin //Инициализация. Result := false; OtherFlist := TFlistFormat.Create; //Ок. Теперь попробуем открыть файлег с "текущей" инфой... if OtherFlist.Load(OtherFlistFile) = true then begin //Если удалося вообще открыть файло то запускаем собсно функцию, выполняющую всю работу... Result := Self.CompareWithFlist(OtherFlist,ListDirName,ComparedList); end; //Чистим мусор... OtherFlist.Free; end; function TFlistFormat.ConvertToSimpleComparedList(var ComparedList : AComparedList) : Boolean; //Превратить список сравнения в нечно упрощенное - без лишней инфы про то как именно что различается, когда важен сам факт наличия различия. var I : Integer; begin //Инициализация. Result := false; //Просто пробежаться по массиву и выполнить преобразования.... for I := 0 to Length(ComparedList)-1 do begin if (ComparedList[I].CheckResult = Flist_CheckFile_MismatchedCRC32) or (ComparedList[I].CheckResult = Flist_CheckFile_MismatchedSize) then begin ComparedList[I].CheckResult := Flist_CheckFile_NonIdentical; end; end; //Готово. Result := true; end; procedure TFlistFormat.ParseUsedGroups(const GroupsStr : AnsiString); //Пропарсить строку с группами и забить инфу в объект. var TempGroupsStr, Parsed : AnsiString; begin //Чистим инфу о группах в объекте... Self.UsedGroups.Clear; //Делаем копию аргумента ибо он ща будет злобно покромсан... TempGroupsStr := GroupsStr; //Собсно кромсаем строчку и заносим покромсанную инфу в список. repeat Parsed := Parse(TempGroupsStr,','); UsedGroups.Add(Parsed); until Length(TempGroupsStr) = 0; end; function TFlistFormat.GenUsedGroups : AnsiString; //Используя инфу в объекте сгенерить строку с группами. var I : Integer; begin //Инициализация Result := ''; //Тупо пробегаем по массиву и добавляем инф... for I := 0 to Self.UsedGroups.Count-1 do begin if Length(Result) > 0 then Result := Result+','; //Добавляет разделитель если нужно. Result := Result+Self.UsedGroups.Strings[I]; //Добавляет собсно имя группы. end; end; procedure TFlistFormat.UpdateUsedGroups; //Просмотреть список и сгенерировать новую инфу о группах, записать её в объект. var I, J : Integer; Finded : Boolean; begin //Имеется 2 варианта поведения: if Self.UseGroups = false then begin //Если группы не используются - просто чистим списог. Self.UsedGroups.Clear; end else begin //Если же группы в наличии - сканим список файлов, новую, ранее не встречавщуюся группу добавляем. Self.UsedGroups.Clear; //Но в начале список всеравно чистим :). //Собсно пробегаемся по списку... for I := 0 to Self.FilesNum-1 do begin //Проверяем есть ли уже группа этого файла в списке. Finded := false; for J := 0 to UsedGroups.Count-1 do begin if FileList[I].Group = UsedGroups.Strings[J] then begin //Нашли, есть уже такая группа в списке. Finded := true; Break; end; end; //Если группа в списке не найдена - добавляем. if Finded = false then begin UsedGroups.Add(FileList[I].Group); end; end; //for I := 0 to Self.FilesNum-1 do begin end; //else begin --> if Self.UseGroups = false then <-- end; procedure TFlistFormat.TurnUseGroupsOn; //Включить режим с использованием групп. var I, J : Integer; Finded : Boolean; begin //Смысл что-то делать есть только если группы вообще не используются. if ReallyUseGroups = false then begin //Нужно пробежаться по всем файлам и если у них там не вписана группа - поставить all. //Ну и, в принципе, само составление списка групп тоже здесь. UsedGroups.Clear; for I := 0 to FilesNum-1 do begin //Смотрим, есть ли у файла группа? if Length(FileList[I].Group) = 0 then begin //Группа не прописана - прописываем all FileList[I].Group := 'all'; end; //Теперь - тупые действия по обновлению списка групп. Finded := false; for J := 0 to UsedGroups.Count-1 do begin if FileList[I].Group = UsedGroups.Strings[J] then begin //Нашли, есть уже такая группа в списке. Finded := true; Break; end; end; //Если группа в списке не найдена - добавляем. if Finded = false then begin UsedGroups.Add(FileList[I].Group); end; end; //Так, группы включены - можно отметить что это действительно так. Self.ReallyUseGroups := true; end; end; procedure TFlistFormat.TurnUseGroupsOff; //Выключить режим использования групп. begin end; //////////////////////////////////////////////////// // Функции в прямом, открытом виде %). // //////////////////////////////////////////////////// function Flist_CheckFile_2Text(const Code : Byte) : AnsiString; //Сгенерить текстовую строку из кода Flist_CheckFile_* begin Result := 'Unknown check-result code.'; if Code = Flist_CheckFile_Identical then begin Result := 'Identical.'; end else if Code = Flist_CheckFile_MismatchedCRC32 then begin Result := 'Mismatched CRC32.'; end else if Code = Flist_CheckFile_MismatchedSize then begin Result := 'Mismatched size.'; end else if Code = Flist_CheckFile_ElementNotExists then begin Result := 'Not exists in Flist.'; end else if Code = Flist_CheckFile_FileNotExists then begin Result := 'Disappeared.'; end else if Code = Flist_CheckFile_NothingExists then begin Result := 'Not exists.'; end else if Code = Flist_CheckFile_Error then begin Result := 'Unknown error.'; end; end; end.