/////////////////////////////////////////////////////////// // 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) //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////// // Модуль для работы с файлами *.iss // // Это скрипты для сборки инсталляций // // с помощью InnoSetup compiler // //////////////////////////////////////////////////// unit IssFormat; {$mode objfpc}{$H+} interface uses Classes, SysUtils, LCLAnIniFile, ExtraFunctionsLcl, FlistFormat, PascalCodeModifier, ExtraFileUtilsLcl; const //Типо константы-настройки... в теории когда нить можно вынести это дело в какой нить конфег если очень понадобится. IssF_CurrMainComponentName = 'CurrVersion_Main'; //Имя компонента с "текущей" полной версией. IssF_AlwaysInstallComponentName = 'AlwaysInstall'; //Имя компонента для обязательной установки. IssF_FilesAddFlags = true; //Добавлять ли флаги на добавляемые файлы. IssF_DirsAddFlags = false; //Добавлять ли флаги на добавляемые директории. IssF_FilesAddingFlags = 'overwritereadonly replacesameversion sortfilesbyextension'; //Собсно флаги, добавляемые для добавляемых файлов. IssF_DirsAddingFlags = ''; //Собсно флаги, добавляемые для добавляемых директорий. //Строки для дополнения [Code] при парсинге. IssF_PreCode = 'Program InnoScript;'+#13+#10; //То что до кода. IssF_PostCode = #13+#10+'begin end.'; //То что после кода. type TIssFormat = class (TAnIniFile) //Класс для работы со скриптами (*.iss) для Inno Setup Compiler. В принципе просто как набор дополнительных методов... private PasModifier : TPascalCodeModifier; PasModifIsActual : Boolean; //Закрытые методы. function ElementListHas(const ElementList, CheckableComponent : AnsiString) : Boolean; //Проверить - содержится ли имя элемента в списке. function UpdatePasModifier() : Boolean; //Заполнить PasModifier инфой из секции [Code]. function SaveFromPasModifier() : Boolean; //Вернуть инфу из PasModifier обратно в [Code]. public //Переменные. ErrorMessage : AnsiString; //Конструкторы-деструкторы... Constructor Create; override; Destructor Destroy; override; //Открытые методы. procedure MakNewFile(); override; //Очищает инфу класса. Procedure RemoveSection(const SectName : string); override; //Полностью удалить секцию. Procedure WriteRawSection(const SectName, SectContent : string); override; //Перезаписать всё содержимое секции из строки. procedure ClearFiles(); //Удалить секции про файлы - в т.ч. про удаляемые файлы. procedure ClearIcons(); //Удалить секцию про иконки. procedure ClearRegistry(); //Удалить секцию про реестр. function UpdateInfoToVerUpdate(const OldVerIssFile, OldVerCompName : AnsiString) : Boolean; //Добавить инфу по изменению иконок и реестра для обновления с более старой версии. function AddFilesFromFlist(var FlistFile : TFlistFormat ; const ComponentName : AnsiString) : Boolean; //Добавить устанавливаемые файлы из Flist-а для указанного компонента. function AddFilesFromFlist(const FlistFileName, ComponentName : AnsiString) : Boolean; //Добавить устанавливаемые файлы из Flist-а для указанного компонента. function AddFilesFromDir(const DirName, ComponentName : AnsiString) : Boolean; //Добавить устанавливаемые файлы из Flist-a для указанного компонента. function AddNewFilesFromFlists(var OldFlistFile, NewFlistFile : TFlistFormat; const ComponentName : AnsiString) : Boolean; //Добавить устанавливаемые файлы из сравнения старого и нового FList-а для указанного компонента. function AddNewFilesFromFlists(const OldFlistFileName, NewFlistFileName, ComponentName : AnsiString) : Boolean; //Добавить устанавливаемые файлы из сравнения старого и нового FList-а для указанного компонента. function AddNewFilesFromDirs(const OldDirName, NewDirName, ComponentName : AnsiString) : Boolean; //Добавить устанавливаемые файлы из сравнения старой и новой диры для указанного компонента. function ReadCodeConstValue(const ConstName : AnsiString) : AnsiString; //Прочитать значение конкретной константы. function WriteCodeConstValue(const ConstName, ConstValue : AnsiString) : Boolean; //Записать значение конкретной константы. function CodeFunctionExists(const FunctionName : AnsiString) : Boolean; //Существует ли такая функция в коде? function WriteCodeFunction(const FunctionName, FunctionContent : AnsiString) : Boolean; //Записать функцию в Code, если уже есть - заменить. function InstallTypeExists(const InstallTypeName : AnsiString) : Boolean; //Существует ли тип инсталляции? function RemoveInstalType(const InstallTypeName : AnsiString) : Boolean; //Удалить тип инсталляхи. false вернет если типа с таким именем не было, иначе всегда true. procedure WriteInstallType(const InstallTypeName, FullInstTypeString : AnsiString); //Вписать тип инсталляции, если нужно - заменить имеющийся с тем же именем. FullInstTypeString - полная строчка для секции [Types]. function ComponentExists(const ComponentName : AnsiString) : Boolean; //Существует ли такой компонент? function RemoveComponent(const ComponentName : AnsiString) : Boolean; //Удалить компонент. false вернет если такого компонента и не было, во всех остальных случаях возвращает true. procedure WriteComponent(const ComponentName, FullComponentString : AnsiString); //Вписать компонент, если нужно - заменить существующий с таким же именем. FullComponentString - полная строчка для секции [Components]. //Открытые методы для чтения\записи каких-либо значений стандартных секций. function GetSourceDir() : AnsiString; //Прочитать SourceDir. function GetOutputDir() : AnsiString; //Прочитать OutputDir. procedure SetSourceDir(const SourceDir : AnsiString); //Записать SourceDir. procedure SetOutputDir(const OutputDir : AnsiString); //Записать OutputDir. end; implementation ///////////////////////////////////////////// // TIssFormat // ///////////////////////////////////////////// //-----------------------------------------// // Конструкторы-деструкторы... // //-----------------------------------------// Constructor TIssFormat.Create; begin //Конструктор. Во избежание глюков. inherited; //Инициализировать вложенные объекты. Self.PasModifier := TPascalCodeModifier.Create; //Вызов начальной очистки. Self.MakNewFile(); end; Destructor TIssFormat.Destroy; begin //Типа деструктор. Self.MakNewFile(); inherited; end; //------------------------------------------// // Закрытые методы... // //------------------------------------------// function TIssFormat.ElementListHas(const ElementList, CheckableComponent : AnsiString) : Boolean; //Проверить - содержится ли имя элемента в списке. var TempList, TempValue, TempCheck : AnsiString; begin //Инициализация. Result := false; TempCheck := LowerCase(CheckableComponent); TempList := Trim(ElementList); //Проверка... repeat if TempList <> '' then begin TempValue := LowerCase(Parse(TempList,' ')); TempList := Trim(TempList); if TempValue = TempCheck then begin //Нашли. Result := true; TempList := ''; end; end; until TempList = ''; end; function TIssFormat.UpdatePasModifier() : Boolean; //Заполнить PasModifier инфой из секции [Code]. begin //Инициализация. Result := true; //Собсно апдейтим. if Self.PasModifIsActual = false then begin //Если есть такая необходимость. if Self.SectionExists('Code') = true then begin //Есть такая секция. Читаем. Self.PasModifier.MakNewFile; Self.PasModifier.FileString := IssF_PreCode+Self.ReadRawSection('Code')+IssF_PostCode; try if Self.PasModifier.ReParseFile() = false then begin //Сюда попадет если была обработанная уже ошибка в ReParseFile. Self.ErrorMessage := 'TPascalCodeModifier, while parsing error: '+Self.PasModifier.ErrorMessage; Result := false; Exit; end; except //Если была ошибка парсинга on E: Exception do begin Self.ErrorMessage := 'TPascalCodeModifier, while parsing error: '+E.Message; Result := false; Exit; end; end; //Прочитали. Self.PasModifIsActual := true; end; end; end; function TIssFormat.SaveFromPasModifier() : Boolean; //Вернуть инфу из PasModifier обратно в [Code]. begin //Инициализация. Result := true; if Self.PasModifIsActual = true then begin //Ога, есть чего забросить. Забрасываем. Self.WriteRawSection('Code', Copy(Self.PasModifier.FileString, Length(IssF_PreCode)+1, Length(Self.PasModifier.FileString)-(Length(IssF_PreCode)+Length(IssF_PostCode))) ); Self.PasModifIsActual := true; end; end; //------------------------------------------// // Открытые методы... // //------------------------------------------// procedure TIssFormat.MakNewFile(); //Очищает инфу класса. begin inherited; Self.PasModifier.MakNewFile; Self.PasModifIsActual := false; ErrorMessage := ''; end; Procedure TIssFormat.RemoveSection(const SectName : string); //Полностью удалить секцию. begin //Смысл оверрайдинга - чтобы отслеживать изменения секции [Code]. //Сначала оригинальная функция, без изменений... inherited; //И если это [Code] то сделать отметку. if (LowerCase(SectName) = 'code') and (Self.PasModifIsActual = true) then begin Self.PasModifIsActual := false; end; end; Procedure TIssFormat.WriteRawSection(const SectName, SectContent : string); //Перезаписать всё содержимое секции из строки. begin //Смысл оверрайдинга - чтобы отслеживать изменения секции [Code]. //Сначала оригинальная функция, без изменений... inherited; //И если это [Code] то сделать отметку. if (LowerCase(SectName) = 'code') and (Self.PasModifIsActual = true) then begin Self.PasModifIsActual := false; end; end; procedure TIssFormat.ClearFiles(); //Удалить секции про файлы - в т.ч. про удаляемые файлы. begin //Итак - под действие этой функции попадают секции [Dirs], [Files], [InstallDelete]. Чистим их. Self.WriteRawSection('Dirs',''); Self.WriteRawSection('Files',''); Self.WriteRawSection('InstallDelete',''); end; procedure TIssFormat.ClearIcons(); //Удалить секцию про иконки. begin //Под действие данной функции попадает секция [Icons]. Self.WriteRawSection('Icons',''); end; procedure TIssFormat.ClearRegistry(); //Удалить секцию про реестр. begin //Под действие данной функции попадает секция [Registry]. Self.WriteRawSection('Registry',''); end; function TIssFormat.UpdateInfoToVerUpdate(const OldVerIssFile, OldVerCompName : AnsiString) : Boolean; //Добавить инфу по изменению иконок и реестра для обновления с более старой версии. var OldIconList, NewIconList, DeleteList, OldRegistryList, NewRegistryList : AIniValueList; OldIssFile : TIssFormat; I, oI : Integer; NewCheckableElem, ElemIsAddable : Boolean; begin //Инициализация... Result := false; OldIssFile := TIssFormat.Create; SetLength(OldIconList,0,0); SetLength(NewIconList,0,0); SetLength(DeleteList,0,0); SetLength(OldRegistryList,0,0); SetLength(NewRegistryList,0,0); //Пытаемся прочитать "старый" файлег... if OldIssFile.Load(OldVerIssFile) = true then begin //Для начала прочитаем иконки с текущего файла... Self.ReadIniValueList('Icons',NewIconList); //Ок. Теперь читаем иконки из "старого" файла... OldIssFile.ReadIniValueList('Icons',OldIconList); //Ок. Теперь прочитаем команды на удаление с текущего файла... Self.ReadIniValueList('InstallDelete',DeleteList); //Читаем инфу про реестр с текущего файла... Self.ReadIniValueList('Registry',NewRegistryList); //Читаем инфу про реестр из "старого" файла. OldIssFile.ReadIniValueList('Registry',OldRegistryList); //Обрабатываем иконки: if (Length(OldIconList) > 0) and (Length(NewIconList) > 0) then begin //Если там есть хоть одна иконка - сканим... //Для начала - добавим в новый файл те иконки которых нет в старом... //Считается что иконки текущей версии (и в старой и в новой) - те что с компонентом "CurrVersion_Main" - ищем такие какие есть в новом файле и нет в старом. for I := 0 to Length(NewIconList)-1 do begin //Индикатор - добавлять ли иконку в конце интерации цикла. ElemIsAddable := false; //Проверяем - относится ли иконка к "текущему" компоненту. NewCheckableElem := ElementListHas(GetIniValueListValue(NewIconList,I,'Components'),IssF_CurrMainComponentName); //Если иконка относится к нужному компоненту - то ищем - есть ли такая же и в старом списке, если нет то её надо добавить. if NewCheckableElem = true then begin //Устанавливаем идикатор... ElemIsAddable := true; //По умолчанию считаем что иконку эту нужно добавить в компонент, проверка ниже может это отменить. //Собсно проверяем второй список. for oI := 0 to Length(OldIconList)-1 do begin //Все предельно просто. Запись о иконке должна быть идентична тому что в новом списке, вот и все. Если идентичной не обнаружено то подлежит добавлению. //Допустимо различие только регистра в имени самого файла. if Self.CompareIniValueElements(NewIconList,OldIconList, I, oI, true, 'Filename') = true then begin if Self.CompareIniValueElements(NewIconList,OldIconList, I, oI, false, 'all, Filename') = true then begin //Идентичны - иконка добавлению в компонент не подлежит. ElemIsAddable := false; end; end; end; end; //Если в итоге решили, что иконку надо добавить - добавляем. if ElemIsAddable = true then begin //Увеличиваем размер массива... SetLength(NewIconList,Length(NewIconList)+1); //Копируем элемент... Self.CopyIniValueListElement(NewIconList,NewIconList,I,Length(NewIconList)-1); //Меняем компонент иконке... Self.ChangeIniValueListValue(NewIconList,Length(NewIconList)-1,'Components',OldVerCompName); end; end; //Ок, добавили. Теперь добавим в новый файл команды на удаление тех иконок, которые были в старом файле, но исчезли в новом. for I := 0 to Length(OldIconList)-1 do begin //Индикатор - добавлять ли иконку (команду на её удаление) в конце интерации цикла. ElemIsAddable := false; //Проверяем - относится ли иконка к "текущему" компоненту, либо к AlwaysInstall, которые тоже надо удалять от старых версий. NewCheckableElem := (ElementListHas(GetIniValueListValue(OldIconList,I,'Components'),IssF_CurrMainComponentName)) or (ElementListHas(GetIniValueListValue(OldIconList,I,'Components'),IssF_AlwaysInstallComponentName)); //Если иконка относится к нужному компоненту - то ищем - есть ли такая же и в новом списке, если нету - добавляем команду на удаление. if NewCheckableElem = true then begin //Устанавливаем идикатор... ElemIsAddable := true; //По умолчанию считаем что иконку эту нужно удалять при инсталляции... //Собсно проверяем "новый" список. for oI := 0 to Length(NewIconList)-1 do begin //Все предельно просто. Запись о иконке должна быть идентична тому что в новом списке, вот и все. Если идентичной не обнаружено то подлежит удалению. //Допустимо различие только регистра в имени самого файла. if Self.CompareIniValueElements(OldIconList,NewIconList, I, oI, true, 'Filename') = true then begin if Self.CompareIniValueElements(OldIconList,NewIconList, I, oI, false, 'all, Filename') = true then begin //Идентичны - иконка удалению не подлежит. ElemIsAddable := false; end; end; end; end; //Если в итоге решили, что надо добавить команду на удаление иконки - добавляем. if ElemIsAddable = true then begin //Увеличиваем размер массива... SetLength(DeleteList,Length(DeleteList)+1); //Добавляем в новый элемент 3 нужных значений команды: SetLength(DeleteList[Length(DeleteList)-1],3); DeleteList[Length(DeleteList)-1,0].Name := 'Name'; DeleteList[Length(DeleteList)-1,0].Value := GetIniValueListValue(OldIconList,I,'Name'); DeleteList[Length(DeleteList)-1,1].Name := 'Type'; DeleteList[Length(DeleteList)-1,1].Value := 'files'; DeleteList[Length(DeleteList)-1,2].Name := 'Components'; DeleteList[Length(DeleteList)-1,2].Value := OldVerCompName; end; end; //Ок, команды добавлены в списки в памяти. Вполне возможно что списки (NewIconList и DeleteList) изменились - сохраняем их обратно в файл. Self.WriteIniValueList('Icons',NewIconList); Self.WriteIniValueList('InstallDelete',DeleteList); end; //Иконки обработаны. Обрабатываем реестр, по почти аналогичному принципу... if (Length(OldRegistryList) > 0) and (Length(NewRegistryList) > 0) then begin //Для начала - добавим в новый файл те записи реестре, которых нет в старом... for I := 0 to Length(NewRegistryList)-1 do begin //Индикатор - добавлять ли запись в конце интерации цикла. ElemIsAddable := false; //Проверяем - относится ли запись к "текущему" компоненту. NewCheckableElem := ElementListHas(GetIniValueListValue(NewRegistryList,I,'Components'),IssF_CurrMainComponentName); //Если запись относится к нужному компоненту - то ищем - есть ли такая же и в старом списке, если нет то её надо добавить. if NewCheckableElem = true then begin //Устанавливаем идикатор... ElemIsAddable := true; //По умолчанию считаем что запись эту нужно добавить в компонент, проверка ниже может это отменить. //Собсно проверяем второй список. for oI := 0 to Length(OldRegistryList)-1 do begin //Все предельно просто. Запись о записи %) должна быть идентична тому что в новом списке, вот и все. Если идентичной не обнаружено то подлежит добавлению. if Self.CompareIniValueElements(NewRegistryList,OldRegistryList, I, oI, true, '') = true then begin //Идентичны - запись добавлению в компонент не подлежит. ElemIsAddable := false; end; end; end; //Если в итоге решили, что запись надо добавить - добавляем. if ElemIsAddable = true then begin //Увеличиваем размер массива... SetLength(NewRegistryList,Length(NewRegistryList)+1); //Копируем элемент... Self.CopyIniValueListElement(NewRegistryList,NewRegistryList,I,Length(NewRegistryList)-1); //Меняем компонент иконке... Self.ChangeIniValueListValue(NewRegistryList,Length(NewRegistryList)-1,'Components',OldVerCompName); end; end; //Ок, добавили. Теперь добавим в новый файл команды на удаление тех записей, которые были в старом файле, но исчезли в новом, являются либо создавабельными ключами, либо создавабельными (не none) значениями. for I := 0 to Length(OldRegistryList)-1 do begin //Индикатор - добавлять ли команду на удаление в конце интерации цикла. ElemIsAddable := false; //Проверяем - относится ли запись к "текущему" компоненту, либо к AlwaysInstall, которые тоже надо удалять от старых версий. NewCheckableElem := (ElementListHas(GetIniValueListValue(OldRegistryList,I,'Components'),IssF_CurrMainComponentName)) or (ElementListHas(GetIniValueListValue(OldRegistryList,I,'Components'),IssF_AlwaysInstallComponentName)); //Если запись относится к нужному компоненту - то ищем - есть ли такая же и в новом списке, если нету - добавляем команду на удаление (разную, смотря что за запись, может быть и не нужно удалять, а если и нужно то можно по разному). if NewCheckableElem = true then begin //Устанавливаем идикатор... ElemIsAddable := true; //По умолчанию считаем что иконку эту нужно удалять при инсталляции... //Собсно проверяем "новый" список. for oI := 0 to Length(NewRegistryList)-1 do begin //Все предельно просто. Запись должна быть идентична тому что в новом списке, вот и все. Если идентичной не обнаружено то подлежит удалению. if Self.CompareIniValueElements(OldRegistryList,NewRegistryList, I, oI, true, '') = true then begin //Идентичны - запись удалению не подлежит. ElemIsAddable := false; end; end; end; //Если в итоге решили, что надо добавить команду на удаление иконки - добавляем. if ElemIsAddable = true then begin //В зависимости от того что из себя запись представляет - и таки надо ли её все-же удалять % ))). //Ключ ли это? if Self.IniValueListValueExists(OldRegistryList,I,'ValueType') = false then begin //Да, это ключ. А не команда ли это на удаление? if ElementListHas(GetIniValueListValue(OldRegistryList,I,'Flags'),'deletekey') = false then begin //Не, нифига. Обычная создавалка ключа. Тогда собсно и добавляем команду-удалялку: //Увеличиваем размер массива... SetLength(NewRegistryList,Length(NewRegistryList)+1); //Добавляем в новый элемент 4 нужных значений команды: SetLength(NewRegistryList[Length(NewRegistryList)-1],4); NewRegistryList[Length(NewRegistryList)-1,0].Name := 'Root'; NewRegistryList[Length(NewRegistryList)-1,0].Value := GetIniValueListValue(OldRegistryList,I,'Root'); NewRegistryList[Length(NewRegistryList)-1,1].Name := 'Subkey'; NewRegistryList[Length(NewRegistryList)-1,1].Value := GetIniValueListValue(OldRegistryList,I,'Subkey'); NewRegistryList[Length(NewRegistryList)-1,2].Name := 'Flags'; NewRegistryList[Length(NewRegistryList)-1,2].Value := 'deletekey'; NewRegistryList[Length(NewRegistryList)-1,3].Name := 'Components'; NewRegistryList[Length(NewRegistryList)-1,3].Value := OldVerCompName; end else begin //Ага, удалялка, тогда она нам нафег не сдалась, фтопку её, ничО не делать и все. end; end else begin //Нет, это значение. А не none ли оно случаем? if LowerCase(GetIniValueListValue(OldRegistryList,I,'ValueType')) <> 'none' then begin //Не, оно не none - проверяем, нету ли там случаем команды на удаление значения? if ElementListHas(GetIniValueListValue(OldRegistryList,I,'Flags'),'deletevalue') = false then begin //Ага, надо добавить удалялку значения - собсно и добавляем: //Увеличиваем размер массива... SetLength(NewRegistryList,Length(NewRegistryList)+1); //Добавляем в новый элемент 6 нужных значений команды: SetLength(NewRegistryList[Length(NewRegistryList)-1],6); NewRegistryList[Length(NewRegistryList)-1,0].Name := 'Root'; NewRegistryList[Length(NewRegistryList)-1,0].Value := GetIniValueListValue(OldRegistryList,I,'Root'); NewRegistryList[Length(NewRegistryList)-1,1].Name := 'Subkey'; NewRegistryList[Length(NewRegistryList)-1,1].Value := GetIniValueListValue(OldRegistryList,I,'Subkey'); NewRegistryList[Length(NewRegistryList)-1,2].Name := 'ValueType'; NewRegistryList[Length(NewRegistryList)-1,2].Value := 'none'; NewRegistryList[Length(NewRegistryList)-1,3].Name := 'ValueName'; NewRegistryList[Length(NewRegistryList)-1,3].Value := GetIniValueListValue(OldRegistryList,I,'ValueName'); NewRegistryList[Length(NewRegistryList)-1,4].Name := 'Flags'; NewRegistryList[Length(NewRegistryList)-1,4].Value := 'deletevalue'; NewRegistryList[Length(NewRegistryList)-1,5].Name := 'Components'; NewRegistryList[Length(NewRegistryList)-1,5].Value := OldVerCompName; end else begin //Это удалялка значения - нам не неё пофик. end; end else begin //Если оно типа none то нифига делать не надо - пусть оно в некоторых случаях и может создать ключег, но поскольку //это таки команда на создание значения (пусть и бессмысленная и значение не создастся), то мы не имеем права удалять //ключ который может быть создается чем-то там еще и можнт содержать значения, которые эта команда нифига не создавала. end; end; end; end; //Ок, команды добавлены в список в памяти. Вполне возможно что этот список (NewRegistryList) изменился - сохраняем его обратно в файл. Self.WriteIniValueList('Registry',NewRegistryList); end; end; //Готово. Result := true; //Чистим мусор... OldIssFile.Free; SetLength(OldIconList,0,0); SetLength(NewIconList,0,0); SetLength(DeleteList,0,0); SetLength(OldRegistryList,0,0); SetLength(NewRegistryList,0,0); end; function TIssFormat.AddFilesFromFlist(var FlistFile : TFlistFormat ; const ComponentName : AnsiString) : Boolean; //Добавить устанавливаемые файлы из Flist-а для указанного компонента. var DirsList, FilesList : AIniValueList; RealDirsListSize, RealFilesListSize, I, ElemsNum : Integer; TempStr : AnsiString; begin //Инициализация... Result := false; SetLength(DirsList,0,0); SetLength(FilesList,0,0); //Прочитать списки директорий и файлов из текущего файла... Self.ReadIniValueList('Dirs',DirsList); Self.ReadIniValueList('Files',FilesList); //Выставить ориентировочный размер для списков, исходя из количества элементов в flist. RealDirsListSize := Length(DirsList); RealFilesListSize := Length(FilesList); SetLength(DirsList,RealDirsListSize+FlistFile.FilesNum); SetLength(FilesList,RealFilesListSize+FlistFile.FilesNum); //Ну, собсно погнали по списку flist-а. for I := 0 to FlistFile.FilesNum-1 do begin //Смотрим шё за субж - дира или файло? if FlistFile.FileList[I].FileType = Flist_Filetype_File then begin //Файл. Добавляем. Считаем количество элементов в строке файла. ElemsNum := 3; //Это минимум. if IssF_FilesAddFlags = true then begin //Добавлять флаги. ElemsNum := ElemsNum+1; end; //Собсно выделяем память.. SetLength(FilesList[RealFilesListSize],ElemsNum); //Пишем инфу для добавляемого файла... TempStr := GetLocalPath(FlistFile.GetGlobalRoot(),FlistFile.GetRealElementName(I)); FilesList[RealFilesListSize,0].Name := 'Source'; FilesList[RealFilesListSize,0].Value := RightStr(TempStr,Length(TempStr)-2); FilesList[RealFilesListSize,1].Name := 'DestDir'; FilesList[RealFilesListSize,1].Value := AddLastSlash('{app}'+ExtractFilePath(RightStr(TempStr,Length(TempStr)-1))); FilesList[RealFilesListSize,2].Name := 'Components'; FilesList[RealFilesListSize,2].Value := ComponentName; if IssF_FilesAddFlags = true then begin FilesList[RealFilesListSize,3].Name := 'Flags'; FilesList[RealFilesListSize,3].Value := IssF_FilesAddingFlags; end; //Увеличиваем счетчик количества элементов в массиве. RealFilesListSize := RealFilesListSize+1; end else if FlistFile.FileList[I].FileType = Flist_Filetype_Directory then begin //Директория. Не ссылка ли это на себя? TempStr := GetLocalPath(FlistFile.GetGlobalRoot(),FlistFile.GetRealElementName(I)); if Length(TempStr) > 2 then begin //Да не, нормальная директория. Добавляем. Считаем количество элементов в строке файла. ElemsNum := 2; //Это минимум. if IssF_DirsAddFlags = true then begin //Добавлять флаги. ElemsNum := ElemsNum+1; end; //Собсно выделяем память... SetLength(DirsList[RealDirsListSize],ElemsNum); //Пишем инфу для добавляемой директории... DirsList[RealDirsListSize,0].Name := 'Name'; DirsList[RealDirsListSize,0].Value := DelLastSlash('{app}'+RightStr(TempStr,Length(TempStr)-1)); DirsList[RealDirsListSize,1].Name := 'Components'; DirsList[RealDirsListSize,1].Value := ComponentName; if IssF_DirsAddFlags = true then begin DirsList[RealDirsListSize,2].Name := 'Flags'; DirsList[RealDirsListSize,2].Value := IssF_DirsAddingFlags; end; //Увеличиваем счетчик количества элементов в массиве. RealDirsListSize := RealDirsListSize+1; end; end; end; //Все добавлено. Уменьшаем размеры списков до их реального размера... SetLength(DirsList,RealDirsListSize); SetLength(FilesList,RealFilesListSize); //Записываем полученные списки обратно в файл. Self.WriteIniValueList('Dirs',DirsList); Self.WriteIniValueList('Files',FilesList); //Возвращаем результат... Result := true; //Уборка мусора... SetLength(DirsList,0,0); SetLength(FilesList,0,0); end; function TIssFormat.AddFilesFromFlist(const FlistFileName, ComponentName : AnsiString) : Boolean; //Добавить устанавливаемые файлы из Flist-а для указанного компонента. var FlistFile : TFlistFormat; begin //Инициализация... Result := false; FlistFile := TFlistFormat.Create; //Если flist-файлег есть - читаем его. if FileExists(FlistFileName) = true then begin //Прочитать файлег. FlistFile.Load(FlistFileName); //Проставить корень Flist-у. FlistFile.GlobalRoot := Self.GetSourceDir(); //Вызвать функцию реально выполняющую задачу. Result := Self.AddFilesFromFlist(FlistFile,ComponentName); end; //Уборка мусора... FlistFile.Free; end; function TIssFormat.AddFilesFromDir(const DirName, ComponentName : AnsiString) : Boolean; //Добавить устанавливаемые файлы из Flist-a для указанного компонента. var FlistFile : TFlistFormat; begin //Инициализация... Result := false; FlistFile := TFlistFormat.Create; //Настройка... в основном в целях оптимизации - нефиг читать инфу и делать преобразования например тех же путей которые никогда не понадобятся. FlistFile.PathsAreAbsolute := true; FlistFile.AddSize := false; FlistFile.AddCRC32 := false; FlistFile.GlobalRoot := Self.GetSourceDir(); //Читаем инфу про директорию в объект flist-а. if FlistFile.AddRealDirectory(DirName,true) = true then begin //Если все прочитано успешно - вызвать функцию реально выполняющую задачу. Result := Self.AddFilesFromFlist(FlistFile,ComponentName); end; //Уборка мусора... FlistFile.Free; end; function TIssFormat.AddNewFilesFromFlists(var OldFlistFile, NewFlistFile : TFlistFormat; const ComponentName : AnsiString) : Boolean; //Добавить устанавливаемые файлы из сравнения старого и нового FList-а для указанного компонента. var ComparedList : AComparedList; DirsList, FilesList, DeleteList, DirDeleteList : AIniValueList; RealDirsListSize, RealFilesListSize, RealDeleteListSize, RealDirDeleteListSize, I, ElemsNum : Integer; //TestStr : AnsiString; begin //Инициализация. Result := false; SetLength(ComparedList,0); SetLength(DirsList,0,0); SetLength(FilesList,0,0); SetLength(DeleteList,0,0); SetLength(DirDeleteList,0,0); //Прочитать списки из текущего файла... Self.ReadIniValueList('Dirs',DirsList); Self.ReadIniValueList('Files',FilesList); Self.ReadIniValueList('InstallDelete',DeleteList); //Cравниваем файлы. OldFlistFile.CompareWithFlist(NewFlistFile,'.\',ComparedList); //И упрощаем список сравнения дабы не усложнять здешний код без нужды. OldFlistFile.ConvertToSimpleComparedList(ComparedList); //Выставить ориентировочный размер для списков, исходя из количества элементов в ComparedList. RealDirsListSize := Length(DirsList); RealFilesListSize := Length(FilesList); RealDeleteListSize := Length(DeleteList); RealDirDeleteListSize := 0; SetLength(DirsList,RealDirsListSize+Length(ComparedList)); SetLength(FilesList,RealFilesListSize+Length(ComparedList)); SetLength(DeleteList,RealDeleteListSize+Length(ComparedList)); SetLength(DirDeleteList,Length(ComparedList)); //Теперь пробегаемся по массиву, добавляем команды на удаление файлов\директорий и на установку новых. for I := 0 to Length(ComparedList)-1 do begin //TestStr := ComparedList[I].FilePath; if ComparedList[I].CheckResult <> Flist_CheckFile_Identical then begin //Обнаружено несоответствие. Смотрим - чего за субж. Для начала - файл это или директория. if ComparedList[I].FileType = Flist_Filetype_File then begin //Файл. Смотрим чего с ним надо сделать. if (ComparedList[I].CheckResult = Flist_CheckFile_NonIdentical) or (ComparedList[I].CheckResult = Flist_CheckFile_ElementNotExists) then begin //Файл либо изменился, либо появился там где его не было - добавляем его в инсталляху. //Считаем количество элементов в строке файла. ElemsNum := 3; //Это минимум. if IssF_FilesAddFlags = true then begin //Добавлять флаги. ElemsNum := ElemsNum+1; end; //Собсно выделяем память.. SetLength(FilesList[RealFilesListSize],ElemsNum); //Пишем инфу для добавляемого файла... FilesList[RealFilesListSize,0].Name := 'Source'; FilesList[RealFilesListSize,0].Value := RightStr(ComparedList[I].FilePath,Length(ComparedList[I].FilePath)-2); FilesList[RealFilesListSize,1].Name := 'DestDir'; FilesList[RealFilesListSize,1].Value := AddLastSlash('{app}'+ExtractFilePath(RightStr(ComparedList[I].FilePath,Length(ComparedList[I].FilePath)-1))); FilesList[RealFilesListSize,2].Name := 'Components'; FilesList[RealFilesListSize,2].Value := ComponentName; if IssF_FilesAddFlags = true then begin FilesList[RealFilesListSize,3].Name := 'Flags'; FilesList[RealFilesListSize,3].Value := IssF_FilesAddingFlags; end; //Увеличиваем счетчик количества элементов в массиве. RealFilesListSize := RealFilesListSize+1; end else if ComparedList[I].CheckResult = Flist_CheckFile_FileNotExists then begin //Файло исчезло в новой версии. Добавляем команду на удаление файла %). //Добавляем в новый элемент 3 нужных значений команды: SetLength(DeleteList[RealDeleteListSize],3); //Ну, и собсно сама команда: DeleteList[RealDeleteListSize,0].Name := 'Name'; DeleteList[RealDeleteListSize,0].Value := DelLastSlash('{app}'+RightStr(ComparedList[I].FilePath,Length(ComparedList[I].FilePath)-1)); DeleteList[RealDeleteListSize,1].Name := 'Type'; DeleteList[RealDeleteListSize,1].Value := 'files'; DeleteList[RealDeleteListSize,2].Name := 'Components'; DeleteList[RealDeleteListSize,2].Value := ComponentName; //Увеличиваем счетчик количества элементов в массиве. RealDeleteListSize := RealDeleteListSize+1; end; end else if ComparedList[I].FileType = Flist_Filetype_Directory then begin //Директория. Не ссылка ли это на себя? if Length(ComparedList[I].FilePath) > 2 then begin //Не, нормальная дира, можно обрабатывать дальше. //Тут только 2 варианта - либо директория появилась, либо она исчезла. if ComparedList[I].CheckResult = Flist_CheckFile_ElementNotExists then begin //Появилась новая директория, добавим её. //Считаем количество элементов в строке файла. ElemsNum := 2; //Это минимум. if IssF_DirsAddFlags = true then begin //Добавлять флаги. ElemsNum := ElemsNum+1; end; //Собсно выделяем память... SetLength(DirsList[RealDirsListSize],ElemsNum); //Пишем инфу для добавляемой директории... DirsList[RealDirsListSize,0].Name := 'Name'; DirsList[RealDirsListSize,0].Value := DelLastSlash('{app}'+RightStr(ComparedList[I].FilePath,Length(ComparedList[I].FilePath)-1)); DirsList[RealDirsListSize,1].Name := 'Components'; DirsList[RealDirsListSize,1].Value := ComponentName; if IssF_DirsAddFlags = true then begin DirsList[RealDirsListSize,2].Name := 'Flags'; DirsList[RealDirsListSize,2].Value := IssF_DirsAddingFlags; end; //Увеличиваем счетчик количества элементов в массиве. RealDirsListSize := RealDirsListSize+1; end else if ComparedList[I].CheckResult = Flist_CheckFile_FileNotExists then begin //Директория исчезла. Добавляем команду на её удаление (только для пустой диры ессно). //Причем добавляем команду в отдельный список, который будет добавлен в конец списка //удаления в конце работы функции чтоб директории удалялись уже после всех файлов ибо //должны быть пусты. //Добавляем в новый элемент 3 нужных значений команды: SetLength(DirDeleteList[RealDirDeleteListSize],3); //Ну, и собсно сама команда: DirDeleteList[RealDirDeleteListSize,0].Name := 'Name'; DirDeleteList[RealDirDeleteListSize,0].Value := DelLastSlash('{app}'+RightStr(ComparedList[I].FilePath,Length(ComparedList[I].FilePath)-1)); DirDeleteList[RealDirDeleteListSize,1].Name := 'Type'; DirDeleteList[RealDirDeleteListSize,1].Value := 'dirifempty'; DirDeleteList[RealDirDeleteListSize,2].Name := 'Components'; DirDeleteList[RealDirDeleteListSize,2].Value := ComponentName; //Увеличиваем счетчик количества элементов в массиве. RealDirDeleteListSize := RealDirDeleteListSize+1; end; end; end; end;; end; //Инфа добавлена. Устаканиваем размеры массивов... SetLength(DirsList,RealDirsListSize); SetLength(FilesList,RealFilesListSize); SetLength(DeleteList,RealDeleteListSize); SetLength(DirDeleteList,RealDirDeleteListSize); //Добавляем инфу по удалению директорий в конец общей инфы по удалению... Self.AddIniValueList(DirDeleteList,DeleteList); //И записываем измененную инфу обратно в файл... Self.WriteIniValueList('Dirs',DirsList); Self.WriteIniValueList('Files',FilesList); Self.WriteIniValueList('InstallDelete',DeleteList); //Результат. Result := true; //Очистка мусора. SetLength(ComparedList,0); SetLength(DirsList,0,0); SetLength(FilesList,0,0); SetLength(DeleteList,0,0); SetLength(DirDeleteList,0,0); end; function TIssFormat.AddNewFilesFromFlists(const OldFlistFileName, NewFlistFileName, ComponentName : AnsiString) : Boolean; //Добавить устанавливаемые файлы из сравнения старого и нового FList-а для указанного компонента. var OldFlistFile, NewFlistFile : TFlistFormat; begin //Инициализация... Result := false; OldFlistFile := TFlistFormat.Create; NewFlistFile := TFlistFormat.Create; //Если flist-файлеги есть - читаем их. if (FileExists(OldFlistFileName) = true) and (FileExists(NewFlistFileName) = true) then begin //Прочитать файлеги. OldFlistFile.Load(OldFlistFileName); NewFlistFile.Load(NewFlistFileName); //Проставить корень Flist-у. OldFlistFile.GlobalRoot := Self.GetSourceDir(); NewFlistFile.GlobalRoot := Self.GetSourceDir(); //Вызвать функцию реально выполняющую задачу. Result := Self.AddNewFilesFromFlists(OldFlistFile,NewFlistFile,ComponentName); end; //Уборка мусора... OldFlistFile.Free; NewFlistFile.Free; end; function TIssFormat.AddNewFilesFromDirs(const OldDirName, NewDirName, ComponentName : AnsiString) : Boolean; //Добавить устанавливаемые файлы из сравнения старой и новой диры для указанного компонента. var OldFlistFile, NewFlistFile : TFlistFormat; begin //Инициализация... Result := false; OldFlistFile := TFlistFormat.Create; NewFlistFile := TFlistFormat.Create; //Настройка... OldFlistFile.PathsAreAbsolute := false; OldFlistFile.AddSize := true; OldFlistFile.AddCRC32 := true; OldFlistFile.GlobalRoot := OldDirName; NewFlistFile.PathsAreAbsolute := false; NewFlistFile.AddSize := true; NewFlistFile.AddCRC32 := true; NewFlistFile.GlobalRoot := NewDirName; //Читаем инфу про директории в объекты flist-а. if (OldFlistFile.AddRealDirectory(OldDirName,true) = true) and (NewFlistFile.AddRealDirectory(NewDirName,true) = true) then begin //Если все прочитано успешно - переправить глобальный корень... OldFlistFile.GlobalRoot := Self.GetSourceDir(); NewFlistFile.GlobalRoot := Self.GetSourceDir(); //... и вызвать функцию реально выполняющую задачу. Result := Self.AddNewFilesFromFlists(OldFlistFile,NewFlistFile,ComponentName); end; //Уборка мусора... OldFlistFile.Free; NewFlistFile.Free; end; function TIssFormat.ReadCodeConstValue(const ConstName : AnsiString) : AnsiString; //Прочитать значение конкретной константы. begin //Инициализация. Result := ''; //Подгружаем код. if Self.UpdatePasModifier() = true then begin; //Читаем значение константы. Result := Self.PasModifier.ReadCodeConstValue(ConstName); end; end; function TIssFormat.WriteCodeConstValue(const ConstName, ConstValue : AnsiString) : boolean; //Записать значение конкретной константы. begin //Инициализация. Result := true; //Подгружаем код. if Self.UpdatePasModifier() = true then begin //Есть. Меняем константу. Self.PasModifier.WriteCodeConstValue(ConstName, ConstValue); //И пишем код обратно. Result := Self.SaveFromPasModifier(); end else begin //Обломс, не удалось подгрузить код, текст про ошибку уже записан в мессагу. Result := false; end; end; function TIssFormat.CodeFunctionExists(const FunctionName : AnsiString) : Boolean; //Существует ли такая функция в коде? begin //Инициализация. Result := false; //Подгружаем код. if Self.UpdatePasModifier() = true then begin //Есть. Собсно смотрим чего там с функцией. Result := PasModifier.FunctionIsExists(FunctionName); end; end; function TIssFormat.WriteCodeFunction(const FunctionName, FunctionContent : AnsiString) : Boolean; //Записать функцию в Code, если уже есть - заменить. begin //Инициализация. Result := true; //Подгружаем код. if Self.UpdatePasModifier() = true then begin //Есть. Пишем функцию. if Self.PasModifier.WriteFunction(FunctionName, FunctionContent) = true then begin; //И пишем код обратно. Result := Self.SaveFromPasModifier(); end else begin //Не удалось записать функцию. Result := false; Self.ErrorMessage := 'TPascalCodeModifier - unknown error while trying to write a function '+FunctionName; end; end else begin //Обломс, не удалось подгрузить код, текст про ошибку уже записан в мессагу. Result := false; end; end; function TIssFormat.InstallTypeExists(const InstallTypeName : AnsiString) : Boolean; //Существует ли тип инсталляции? var TypesList : AIniValueList; I : Integer; TempStr : AnsiString; begin //Инициализация. Result := false; SetLength(TypesList,0,0); TempStr := LowerCase(InstallTypeName); //Прочитать список типов... Self.ReadIniValueList('Types',TypesList); //Пробежаться по строкам. if Length(TypesList) > 0 then begin for I := 0 to Length(TypesList)-1 do begin if LowerCase(Self.GetIniValueListValue(TypesList,I,'Name')) = TempStr then begin //О как - нашли этот тип. Возвращаем значение. Result := true; //И вываливаемся из цикла. Break; end; end; end; //Выбросить мусор. SetLength(TypesList,0,0); end; function TIssFormat.RemoveInstalType(const InstallTypeName : AnsiString) : Boolean; //Удалить тип инсталляхи. false вернет если типа с таким именем не было, иначе всегда true. var I : Integer; TypesList : AIniValueList; begin //Инициализация. Result := false; SetLength(TypesList,0,0); //Прочитать список с типами инсталлях. Self.ReadIniValueList('Types',TypesList); //Проверить, есть ли тип инсталляции с таким именем, заодно получить номер элемента. I := Self.GetIniValueListIndexByValue(TypesList,'Name',InstallTypeName); if (I >= 0) and (Length(TypesList) > 1) then begin //Есть такой тип - удаляем его. Обращаю внимание на условие "(Length(TypesList) > 1)" - если //Length(TypesList) = 1 то элемент там такой есть, но он единственный - и проблема в этом //случае решается гораздо более простым способом %). //Увидеть этот способ можно ниже, в else. Self.RemoveIniValueListElement(TypesList,I); //И возвращаем список обратно если в списке вообще еще хоть что-то осталось. Self.WriteIniValueList('Types',TypesList); end else if (I >= 0) and (Length(TypesList) = 1) then begin //Вахъ. Последний элемент, и его надо убить нафег. Достаточно просто убить всю секцию. Self.RemoveSection('Types'); end; //Чистка мусора. SetLength(TypesList,0,0); end; procedure TIssFormat.WriteInstallType(const InstallTypeName, FullInstTypeString : AnsiString); //Вписать тип инсталляции, если нужно - заменить имеющийся с тем же именем. FullInstTypeString - полная строчка для секции [Types]. var TypesList, TempList : AIniValueList; begin //Инициализация. SetLength(TypesList,0,0); SetLength(TempList,0,0); //Смотрим - есть ли там этот тип? if Self.InstallTypeExists(InstallTypeName) = true then begin //Ага, есть, значит надо удалить. Self.RemoveInstalType(InstallTypeName); end; //Прочитать список с типами инсталлях. Self.ReadIniValueList('Types',TypesList); //И добавить туда новую строчку. Self.StrToIniValueList(FullInstTypeString,TempList); Self.AddIniValueList(TempList,TypesList); //Теперь вернуть инфу обратно в секцию. Self.WriteIniValueList('Types',TypesList); //Чистим мусор. SetLength(TypesList,0,0); SetLength(TempList,0,0); end; function TIssFormat.ComponentExists(const ComponentName : AnsiString) : Boolean; //Существует ли такой компонент? var ComponentsList : AIniValueList; I : Integer; TempStr : AnsiString; begin //Инициализация. Result := false; SetLength(ComponentsList,0,0); TempStr := LowerCase(ComponentName); //Прочитать список компонентов... Self.ReadIniValueList('Components',ComponentsList); //Пробежаться по строкам. if Length(ComponentsList) > 0 then begin for I := 0 to Length(ComponentsList)-1 do begin if LowerCase(Self.GetIniValueListValue(ComponentsList,I,'Name')) = TempStr then begin //О как - нашли этот компонент. Возвращаем значение. Result := true; //И вываливаемся из цикла. Break; end; end; end; //Выбросить мусор. SetLength(ComponentsList,0,0); end; function TIssFormat.RemoveComponent(const ComponentName : AnsiString) : Boolean; //Удалить компонент. false вернет если такого компонента и не было, во всех остальных случаях возвращает true. var I : Integer; ComponentsList : AIniValueList; begin //Инициализация. Result := false; SetLength(ComponentsList,0,0); //Прочитать список с компонентами. Self.ReadIniValueList('Components',ComponentsList); //Проверить, есть ли компонент с таким именем, заодно получить номер элемента. I := Self.GetIniValueListIndexByValue(ComponentsList,'Name',ComponentName); if (I >= 0) and (Length(ComponentsList) > 1) then begin //Есть такой компонент - удаляем его. Обращаю внимание на условие "(Length(ComponentsList) > 1)" - если //Length(ComponentsList) = 1 то элемент там такой есть, но он единственный - и проблема в этом //случае решается гораздо более простым способом %). //Увидеть этот способ можно ниже, в else. Self.RemoveIniValueListElement(ComponentsList,I); //И возвращаем список обратно если в списке вообще еще хоть что-то осталось. Self.WriteIniValueList('Components',ComponentsList); end else if (I >= 0) and (Length(ComponentsList) = 1) then begin //Вахъ. Последний элемент, и его надо убить нафег. Достаточно просто убить всю секцию. Self.RemoveSection('Components'); end; //Чистка мусора. SetLength(ComponentsList,0,0); end; procedure TIssFormat.WriteComponent(const ComponentName, FullComponentString : AnsiString); //Вписать компонент, если нужно - заменить существующий с таким же именем. //FullComponentString - полная строчка для секции [Components]. var ComponentsList, TempList : AIniValueList; begin //Инициализация. SetLength(ComponentsList,0,0); SetLength(TempList,0,0); //Смотрим - есть ли там этот компонент? if Self.ComponentExists(ComponentName) = true then begin //Ага, есть, значит надо удалить. Self.RemoveComponent(ComponentName); end; //Прочитать список с компонентами. Self.ReadIniValueList('Components',ComponentsList); //И добавить туда новую строчку. Self.StrToIniValueList(FullComponentString,TempList); Self.AddIniValueList(TempList,ComponentsList); //Теперь вернуть инфу обратно в секцию. Self.WriteIniValueList('Components',ComponentsList); //Чистим мусор. SetLength(ComponentsList,0,0); SetLength(TempList,0,0); end; //----------------------------------------------------------------------------------------// // Открытые методы для чтения\записи каких-либо значений стандартных секций. // //----------------------------------------------------------------------------------------// function TIssFormat.GetSourceDir() : AnsiString; //Прочитать SourceDir. begin Result := Self.ReadString('Setup','SourceDir'); end; function TIssFormat.GetOutputDir() : AnsiString; //Прочитать OutputDir. begin Result := Self.ReadString('Setup','OutputDir'); end; procedure TIssFormat.SetSourceDir(const SourceDir : AnsiString); //Записать SourceDir. begin Self.WriteString('Setup','SourceDir', SourceDir); end; procedure TIssFormat.SetOutputDir(const OutputDir : AnsiString); //Записать OutputDir. begin Self.WriteString('Setup','OutputDir', OutputDir); end; end.