Forráskód Böngészése

Merge branch 'support-utf8nopreamble'

Martijn Laan 1 éve
szülő
commit
d9378dee97

+ 4 - 4
ISHelp/isetup.xml

@@ -164,7 +164,7 @@ Includes integrated support for "deflate", bzip2, and 7-Zip LZMA/LZMA2 file <lin
 <keyword value="Creating Installations" />
 <keyword value="Creating Installations" />
 <body>
 <body>
 
 
-<p>Installations are created by means of <i>scripts</i>, which are ASCII or Unicode (UTF-8 encoded with a BOM) text files with a format somewhat similar to .INI files. (No, it's not as complicated as you might be thinking!).</p>
+<p>Installations are created by means of <i>scripts</i>, which are ASCII or Unicode (UTF-8 encoded) text files with a format somewhat similar to .INI files. (No, it's not as complicated as you might be thinking!).</p>
 
 
 <p>Scripts have an ".iss" (meaning Inno Setup Script) extension. The script controls every aspect of the installation. It specifies which files are to be installed and where, what shortcuts are to be created and what they are to be named, and so on.</p>
 <p>Scripts have an ".iss" (meaning Inno Setup Script) extension. The script controls every aspect of the installation. It specifies which files are to be installed and where, what shortcuts are to be created and what they are to be named, and so on.</p>
 
 
@@ -228,7 +228,7 @@ Source: "MYPROG.EXE"; DestDir: "{app}"
 
 
 <p>By default, scripts use ISPP if available, and .isl files use the built-in preprocessor.</p>
 <p>By default, scripts use ISPP if available, and .isl files use the built-in preprocessor.</p>
 
 
-<p>If an Unicode file is used, it must be UTF-8 encoded with a BOM.</p>
+<p>If an Unicode file is used, it must be UTF-8 encoded.</p>
 
 
 <p><br/><b>See also:</b><br/>
 <p><br/><b>See also:</b><br/>
 <link topic="params">Parameters in Sections</link><br/>
 <link topic="params">Parameters in Sections</link><br/>
@@ -2124,7 +2124,7 @@ Name: "nl"; MessagesFile: "compiler:Languages\Dutch.isl"
 <p>Specifies the name(s) of the .isl file(s) to read the default messages from. The file(s) must be located in your installation's <link topic="sourcedirectorynotes">source directory</link> when running the compiler, unless a fully qualified pathname is specified or the pathname is prefixed by "compiler:", in which case it looks for the file in the compiler directory.</p>
 <p>Specifies the name(s) of the .isl file(s) to read the default messages from. The file(s) must be located in your installation's <link topic="sourcedirectorynotes">source directory</link> when running the compiler, unless a fully qualified pathname is specified or the pathname is prefixed by "compiler:", in which case it looks for the file in the compiler directory.</p>
 <p>Each message file may contain a <link topic="langoptionssection">[LangOptions] section</link>, a <link topic="messagessection">[Messages] section</link>, and a <link topic="custommessagessection">[CustomMessages] section.</link></p>
 <p>Each message file may contain a <link topic="langoptionssection">[LangOptions] section</link>, a <link topic="messagessection">[Messages] section</link>, and a <link topic="custommessagessection">[CustomMessages] section.</link></p>
 <p>When multiple files are specified, they are read in the order they are specified, thus the last message file overrides any language options or messages from previous files. Any language options or messages in the main script override the ones from message files.</p>
 <p>When multiple files are specified, they are read in the order they are specified, thus the last message file overrides any language options or messages from previous files. Any language options or messages in the main script override the ones from message files.</p>
-<p>If an Unicode file is used, it must be UTF-8 encoded with a BOM.</p>
+<p>If an Unicode file is used, it must be UTF-8 encoded.</p>
 <examples>
 <examples>
 <pre>
 <pre>
 MessagesFile: "compiler:Dutch.isl"
 MessagesFile: "compiler:Dutch.isl"
@@ -2769,7 +2769,7 @@ Type: files; Name: "{win}\MYPROG.INI"
 <p>If you don't remember which version you installed, click the "Inno Setup Compiler" shortcut created in the Start Menu. If the version number displayed in its title bar says "(a)" you are running Non Unicode Inno Setup. Otherwise you are running Unicode Inno Setup.</p>
 <p>If you don't remember which version you installed, click the "Inno Setup Compiler" shortcut created in the Start Menu. If the version number displayed in its title bar says "(a)" you are running Non Unicode Inno Setup. Otherwise you are running Unicode Inno Setup.</p>
 <p>For the most part the two versions are used identically, and any differences between them are noted throughout the help file. However, the following overview lists the primary differences:</p>
 <p>For the most part the two versions are used identically, and any differences between them are noted throughout the help file. However, the following overview lists the primary differences:</p>
   <ul>
   <ul>
-  <li>Unicode Inno Setup supports UTF-8 encoded .iss and .isl files. A BOM is required. UTF-16 is not supported.</li>
+  <li>Unicode Inno Setup supports UTF-8 encoded .iss and .isl files. Starting with Inno Setup 6.2.3 an UTF-8 preamble (also called BOM) is no longer required. UTF-16 is not supported.</li>
   <li>Any existing ANSI .isl language files are automatically converted during compilation using the <tt>LanguageCodePage</tt> setting of the language.</li>
   <li>Any existing ANSI .isl language files are automatically converted during compilation using the <tt>LanguageCodePage</tt> setting of the language.</li>
   <li>Any [Messages] and [CustomMessages] entries in existing ANSI .iss script files must to be converted to Unicode manually if the language used a special <tt>LanguageCodePage</tt>.</li>
   <li>Any [Messages] and [CustomMessages] entries in existing ANSI .iss script files must to be converted to Unicode manually if the language used a special <tt>LanguageCodePage</tt>.</li>
   <li>Unicode Inno Setup supports UTF-8 and UTF-16LE encoded .txt files for <tt>LicenseFile</tt>, <tt>InfoBeforeFile</tt>, and <tt>InfoAfterFile</tt>.</li>
   <li>Unicode Inno Setup supports UTF-8 and UTF-16LE encoded .txt files for <tt>LicenseFile</tt>, <tt>InfoBeforeFile</tt>, and <tt>InfoAfterFile</tt>.</li>

+ 13 - 4
ISHelp/isxfunc.xml

@@ -1861,7 +1861,7 @@ end;</pre>
       <function>
       <function>
         <name>LoadStringsFromFile</name>
         <name>LoadStringsFromFile</name>
         <prototype>function LoadStringsFromFile(const FileName: String; var S: TArrayOfString): Boolean;</prototype>
         <prototype>function LoadStringsFromFile(const FileName: String; var S: TArrayOfString): Boolean;</prototype>
-        <description><p>Loads the specified text file into the specified string array. Returns True if successful, False otherwise.</p></description>
+        <description><p>Loads the specified text file into the specified string array. UTF-8 encoded files with or without a preamble (also called BOM) are also supported. Returns True if successful, False otherwise.</p></description>
       </function>
       </function>
       <function>
       <function>
         <name>SaveStringToFile</name>
         <name>SaveStringToFile</name>
@@ -1874,13 +1874,22 @@ end;</pre>
         <name>SaveStringsToFile</name>
         <name>SaveStringsToFile</name>
         <prototype>function SaveStringsToFile(const FileName: String; const S: TArrayOfString; const Append: Boolean): Boolean;</prototype>
         <prototype>function SaveStringsToFile(const FileName: String; const S: TArrayOfString; const Append: Boolean): Boolean;</prototype>
         <description><p>Saves the specified string array to the specified file with ASCII encoding. If Append is True and the specified file already exists, it will be appended to instead of overwritten. Returns True if successful, False otherwise.</p></description>
         <description><p>Saves the specified string array to the specified file with ASCII encoding. If Append is True and the specified file already exists, it will be appended to instead of overwritten. Returns True if successful, False otherwise.</p></description>
-        <seealso><p><link topic="isxfunc_SaveStringsToUTF8File">SaveStringsToUTF8File</link></p></seealso>
+        <seealso><p><link topic="isxfunc_SaveStringsToUTF8File">SaveStringsToUTF8File</link><br />
+<link topic="isxfunc_SaveStringsToUTF8FileNoPreamble">SaveStringsToUTF8FileNoPreamble</link></p></seealso>
       </function>
       </function>
       <function>
       <function>
         <name>SaveStringsToUTF8File</name>
         <name>SaveStringsToUTF8File</name>
         <prototype>function SaveStringsToUTF8File(const FileName: String; const S: TArrayOfString; const Append: Boolean): Boolean;</prototype>
         <prototype>function SaveStringsToUTF8File(const FileName: String; const S: TArrayOfString; const Append: Boolean): Boolean;</prototype>
-        <description><p>Saves the specified string array to the specified file with UTF8 encoding. If Append is True and the specified file already exists, it will be appended to instead of overwritten. Returns True if successful, False otherwise.</p></description>
-        <seealso><p><link topic="isxfunc_SaveStringsToFile">SaveStringsToFile</link></p></seealso>
+        <description><p>Saves the specified string array to the specified file with UTF-8 encoding with a preamble (also called BOM). If Append is True and the specified file already exists, it will be appended to instead of overwritten. Returns True if successful, False otherwise.</p></description>
+        <seealso><p><link topic="isxfunc_SaveStringsToFile">SaveStringsToFile</link><br />
+<link topic="isxfunc_SaveStringsToUTF8FileNoPreamble">SaveStringsToUTF8FileNoPreamble</link></p></seealso>
+      </function>
+      <function>
+        <name>SaveStringsToUTF8FileNoPreamble</name>
+        <prototype>function SaveStringsToUTF8FileNoPreamble(const FileName: String; const S: TArrayOfString; const Append: Boolean): Boolean;</prototype>
+        <description><p>Saves the specified string array to the specified file with UTF-8 encoding without a preamble (also called BOM). If Append is True and the specified file already exists, it will be appended to instead of overwritten. Returns True if successful, False otherwise.</p></description>
+        <seealso><p><link topic="isxfunc_SaveStringsToFile">SaveStringsToFile</link><br />
+<link topic="isxfunc_SaveStringsToUTF8File">SaveStringsToUTF8File</link></p></seealso>
       </function>
       </function>
     </subcategory>
     </subcategory>
     <subcategory>
     <subcategory>

+ 15 - 13
Projects/CompForm.dfm

@@ -203,6 +203,7 @@ object CompileForm: TCompileForm
         Top = 0
         Top = 0
         Hint = 'New Main Script (Ctrl+N)'
         Hint = 'New Main Script (Ctrl+N)'
         ImageIndex = 0
         ImageIndex = 0
+        ImageName = 'document-new'
         OnClick = FNewMainFileClick
         OnClick = FNewMainFileClick
       end
       end
       object OpenMainFileButton: TToolButton
       object OpenMainFileButton: TToolButton
@@ -210,6 +211,7 @@ object CompileForm: TCompileForm
         Top = 0
         Top = 0
         Hint = 'Open Main Script (Ctrl+O)'
         Hint = 'Open Main Script (Ctrl+O)'
         ImageIndex = 1
         ImageIndex = 1
+        ImageName = 'folder-open-filled-arrow-down-right'
         OnClick = FOpenMainFileClick
         OnClick = FOpenMainFileClick
       end
       end
       object SaveButton: TToolButton
       object SaveButton: TToolButton
@@ -217,6 +219,7 @@ object CompileForm: TCompileForm
         Top = 0
         Top = 0
         Hint = 'Save (Ctrl+S)'
         Hint = 'Save (Ctrl+S)'
         ImageIndex = 2
         ImageIndex = 2
+        ImageName = 'save-filled'
         OnClick = FSaveClick
         OnClick = FSaveClick
       end
       end
       object ToolButton4: TToolButton
       object ToolButton4: TToolButton
@@ -231,6 +234,7 @@ object CompileForm: TCompileForm
         Top = 0
         Top = 0
         Hint = 'Compile (Ctrl+F9)'
         Hint = 'Compile (Ctrl+F9)'
         ImageIndex = 3
         ImageIndex = 3
+        ImageName = 'build'
         OnClick = BCompileClick
         OnClick = BCompileClick
       end
       end
       object StopCompileButton: TToolButton
       object StopCompileButton: TToolButton
@@ -239,6 +243,7 @@ object CompileForm: TCompileForm
         Hint = 'Stop Compile (Esc)'
         Hint = 'Stop Compile (Esc)'
         Enabled = False
         Enabled = False
         ImageIndex = 4
         ImageIndex = 4
+        ImageName = 'build-cancel-2'
         OnClick = BStopCompileClick
         OnClick = BStopCompileClick
       end
       end
       object ToolButton7: TToolButton
       object ToolButton7: TToolButton
@@ -253,6 +258,7 @@ object CompileForm: TCompileForm
         Top = 0
         Top = 0
         Hint = 'Run (F9)'
         Hint = 'Run (F9)'
         ImageIndex = 5
         ImageIndex = 5
+        ImageName = 'debug-start-filled'
         OnClick = RRunClick
         OnClick = RRunClick
       end
       end
       object PauseButton: TToolButton
       object PauseButton: TToolButton
@@ -261,6 +267,7 @@ object CompileForm: TCompileForm
         Hint = 'Pause'
         Hint = 'Pause'
         Enabled = False
         Enabled = False
         ImageIndex = 6
         ImageIndex = 6
+        ImageName = 'debug-break-all-filled'
         OnClick = RPauseClick
         OnClick = RPauseClick
       end
       end
       object TerminateButton: TToolButton
       object TerminateButton: TToolButton
@@ -269,6 +276,7 @@ object CompileForm: TCompileForm
         Hint = 'Terminate (Ctrl+F2)'
         Hint = 'Terminate (Ctrl+F2)'
         Enabled = False
         Enabled = False
         ImageIndex = 10
         ImageIndex = 10
+        ImageName = 'debug-stop-filled'
         OnClick = RTerminateClick
         OnClick = RTerminateClick
       end
       end
       object ToolButton10: TToolButton
       object ToolButton10: TToolButton
@@ -284,6 +292,7 @@ object CompileForm: TCompileForm
         Hint = 'Target Setup (Ctrl+Q)'
         Hint = 'Target Setup (Ctrl+Q)'
         Grouped = True
         Grouped = True
         ImageIndex = 7
         ImageIndex = 7
+        ImageName = 'install'
         Style = tbsCheck
         Style = tbsCheck
         OnClick = RTargetClick
         OnClick = RTargetClick
       end
       end
@@ -293,6 +302,7 @@ object CompileForm: TCompileForm
         Hint = 'Target Uninstall (Ctrl+W)'
         Hint = 'Target Uninstall (Ctrl+W)'
         Grouped = True
         Grouped = True
         ImageIndex = 8
         ImageIndex = 8
+        ImageName = 'uninstall'
         Style = tbsCheck
         Style = tbsCheck
         OnClick = RTargetClick
         OnClick = RTargetClick
       end
       end
@@ -308,6 +318,7 @@ object CompileForm: TCompileForm
         Top = 0
         Top = 0
         Hint = 'Help (F1)'
         Hint = 'Help (F1)'
         ImageIndex = 9
         ImageIndex = 9
+        ImageName = 'button-help'
         OnClick = HDocClick
         OnClick = HDocClick
       end
       end
     end
     end
@@ -364,6 +375,10 @@ object CompileForm: TCompileForm
           RadioItem = True
           RadioItem = True
           OnClick = FSaveEncodingItemClick
           OnClick = FSaveEncodingItemClick
         end
         end
+        object FSaveEncodingUTF8NoPreamble: TMenuItem
+          Caption = 'UTF-8 without &BOM'
+          OnClick = FSaveEncodingItemClick
+        end
       end
       end
       object FSaveAll: TMenuItem
       object FSaveAll: TMenuItem
         Caption = 'Sa&ve All'
         Caption = 'Sa&ve All'
@@ -2638,73 +2653,60 @@ object CompileForm: TCompileForm
   end
   end
   object ToolBarVirtualImageList: TVirtualImageList
   object ToolBarVirtualImageList: TVirtualImageList
     AutoFill = True
     AutoFill = True
-    DisabledGrayscale = False
-    DisabledSuffix = '_Disabled'
     Images = <
     Images = <
       item
       item
         CollectionIndex = 0
         CollectionIndex = 0
         CollectionName = 'document-new'
         CollectionName = 'document-new'
-        Disabled = False
         Name = 'document-new'
         Name = 'document-new'
       end
       end
       item
       item
         CollectionIndex = 1
         CollectionIndex = 1
         CollectionName = 'folder-open-filled-arrow-down-right'
         CollectionName = 'folder-open-filled-arrow-down-right'
-        Disabled = False
         Name = 'folder-open-filled-arrow-down-right'
         Name = 'folder-open-filled-arrow-down-right'
       end
       end
       item
       item
         CollectionIndex = 2
         CollectionIndex = 2
         CollectionName = 'save-filled'
         CollectionName = 'save-filled'
-        Disabled = False
         Name = 'save-filled'
         Name = 'save-filled'
       end
       end
       item
       item
         CollectionIndex = 3
         CollectionIndex = 3
         CollectionName = 'build'
         CollectionName = 'build'
-        Disabled = False
         Name = 'build'
         Name = 'build'
       end
       end
       item
       item
         CollectionIndex = 4
         CollectionIndex = 4
         CollectionName = 'build-cancel-2'
         CollectionName = 'build-cancel-2'
-        Disabled = False
         Name = 'build-cancel-2'
         Name = 'build-cancel-2'
       end
       end
       item
       item
         CollectionIndex = 5
         CollectionIndex = 5
         CollectionName = 'debug-start-filled'
         CollectionName = 'debug-start-filled'
-        Disabled = False
         Name = 'debug-start-filled'
         Name = 'debug-start-filled'
       end
       end
       item
       item
         CollectionIndex = 6
         CollectionIndex = 6
         CollectionName = 'debug-break-all-filled'
         CollectionName = 'debug-break-all-filled'
-        Disabled = False
         Name = 'debug-break-all-filled'
         Name = 'debug-break-all-filled'
       end
       end
       item
       item
         CollectionIndex = 7
         CollectionIndex = 7
         CollectionName = 'install'
         CollectionName = 'install'
-        Disabled = False
         Name = 'install'
         Name = 'install'
       end
       end
       item
       item
         CollectionIndex = 8
         CollectionIndex = 8
         CollectionName = 'uninstall'
         CollectionName = 'uninstall'
-        Disabled = False
         Name = 'uninstall'
         Name = 'uninstall'
       end
       end
       item
       item
         CollectionIndex = 9
         CollectionIndex = 9
         CollectionName = 'button-help'
         CollectionName = 'button-help'
-        Disabled = False
         Name = 'button-help'
         Name = 'button-help'
       end
       end
       item
       item
         CollectionIndex = 10
         CollectionIndex = 10
         CollectionName = 'debug-stop-filled'
         CollectionName = 'debug-stop-filled'
-        Disabled = False
         Name = 'debug-stop-filled'
         Name = 'debug-stop-filled'
       end>
       end>
     ImageCollection = LightToolBarImageCollection
     ImageCollection = LightToolBarImageCollection

+ 38 - 14
Projects/CompForm.pas

@@ -209,6 +209,7 @@ type
     FPrint: TMenuItem;
     FPrint: TMenuItem;
     N22: TMenuItem;
     N22: TMenuItem;
     PrintDialog: TPrintDialog;
     PrintDialog: TPrintDialog;
+    FSaveEncodingUTF8NoPreamble: TMenuItem;
     procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
     procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
     procedure FExitClick(Sender: TObject);
     procedure FExitClick(Sender: TObject);
     procedure FOpenMainFileClick(Sender: TObject);
     procedure FOpenMainFileClick(Sender: TObject);
@@ -513,7 +514,7 @@ var
 implementation
 implementation
 
 
 uses
 uses
-  ActiveX, Clipbrd, ShellApi, ShlObj, IniFiles, Registry, Consts, Types, UITypes, Math,
+  ActiveX, Clipbrd, ShellApi, ShlObj, IniFiles, Registry, Consts, Types, UITypes, Math, WideStrUtils,
   PathFunc, CmnFunc, CmnFunc2, FileClass, CompMsgs, TmSchema, BrowseFunc,
   PathFunc, CmnFunc, CmnFunc2, FileClass, CompMsgs, TmSchema, BrowseFunc,
   HtmlHelpFunc, TaskbarProgressFunc,
   HtmlHelpFunc, TaskbarProgressFunc,
   {$IFDEF STATICCOMPILER} Compile, {$ENDIF}
   {$IFDEF STATICCOMPILER} Compile, {$ENDIF}
@@ -1015,7 +1016,7 @@ begin
 
 
   FMainMemo.Filename := '';
   FMainMemo.Filename := '';
   UpdateCaption;
   UpdateCaption;
-  FMainMemo.SaveInUTF8Encoding := False;
+  FMainMemo.SaveEncoding := seUTF8;
   FMainMemo.Lines.Clear;
   FMainMemo.Lines.Clear;
   FModifiedAnySinceLastCompile := True;
   FModifiedAnySinceLastCompile := True;
   FPreprocessorOutput := '';
   FPreprocessorOutput := '';
@@ -1106,7 +1107,7 @@ begin
     end;
     end;
 
 
     if CommandLineWizard then begin
     if CommandLineWizard then begin
-      SaveTextToFile(CommandLineFileName, WizardForm.ResultScript, False);
+      SaveTextToFile(CommandLineFileName, WizardForm.ResultScript, seUtf8);
     end else begin
     end else begin
       NewMainFile;
       NewMainFile;
       FMainMemo.Lines.Text := WizardForm.ResultScript;
       FMainMemo.Lines.Text := WizardForm.ResultScript;
@@ -1125,14 +1126,31 @@ end;
 procedure TCompileForm.OpenFile(AMemo: TCompScintFileEdit; AFilename: String;
 procedure TCompileForm.OpenFile(AMemo: TCompScintFileEdit; AFilename: String;
   const MainMemoAddToRecentDocs: Boolean);
   const MainMemoAddToRecentDocs: Boolean);
 
 
-  function IsStreamUTF8Encoded(const Stream: TStream): Boolean;
+  function GetStreamSaveEncoding(const Stream: TStream): TSaveEncoding;
   var
   var
     Buf: array[0..2] of Byte;
     Buf: array[0..2] of Byte;
   begin
   begin
-    Result := False;
-    if Stream.Read(Buf, SizeOf(Buf)) = SizeOf(Buf) then
-      if (Buf[0] = $EF) and (Buf[1] = $BB) and (Buf[2] = $BF) then
-        Result := True;
+    Result := seAuto;
+    var Size: Integer := Stream.Size;
+    if (Size >= SizeOf(Buf)) and (Stream.Read(Buf, SizeOf(Buf)) = SizeOf(Buf)) and
+       (Buf[0] = $EF) and (Buf[1] = $BB) and (Buf[2] = $BF) then
+      Result := seUTF8
+    else begin
+      Stream.Seek(0, soFromBeginning);
+      var S: AnsiString;
+      SetLength(S, Size);
+      SetLength(S, Stream.Read(S[1], Size));
+      if IsUTF8String(S) then
+        Result := seUTF8NoPreamble;
+    end;
+  end;
+
+  function GetEncoding(const SaveEncoding: TSaveEncoding): TEncoding;
+  begin
+    if SaveEncoding in [seUTF8, seUTF8NoPreamble] then
+      Result := TEncoding.UTF8
+    else
+      Result := nil;
   end;
   end;
 
 
 var
 var
@@ -1145,9 +1163,9 @@ begin
     if AMemo = FMainMemo then
     if AMemo = FMainMemo then
       NewMainFile;
       NewMainFile;
     GetFileTime(Stream.Handle, nil, nil, @AMemo.FileLastWriteTime);
     GetFileTime(Stream.Handle, nil, nil, @AMemo.FileLastWriteTime);
-    AMemo.SaveInUTF8Encoding := IsStreamUTF8Encoded(Stream);
+    AMemo.SaveEncoding := GetStreamSaveEncoding(Stream);
     Stream.Seek(0, soFromBeginning);
     Stream.Seek(0, soFromBeginning);
-    AMemo.Lines.LoadFromStream(Stream);
+    AMemo.Lines.LoadFromStream(Stream, GetEncoding(AMemo.SaveEncoding));
   finally
   finally
     Stream.Free;
     Stream.Free;
   end;
   end;
@@ -1193,7 +1211,7 @@ function TCompileForm.SaveFile(const AMemo: TCompScintFileEdit; const SaveAs: Bo
         [GetLastError]);
         [GetLastError]);
     TempFN := Buf;
     TempFN := Buf;
     try
     try
-      SaveTextToFile(TempFN, AMemo.Lines.Text, AMemo.SaveInUTF8Encoding);
+      SaveTextToFile(TempFN, AMemo.Lines.Text, AMemo.SaveEncoding);
 
 
       { Back up existing file if needed }
       { Back up existing file if needed }
       if FOptions.MakeBackups and NewFileExists(FN) then begin
       if FOptions.MakeBackups and NewFileExists(FN) then begin
@@ -1728,8 +1746,9 @@ var
 begin
 begin
   FSaveMainFileAs.Enabled := FActiveMemo = FMainMemo;
   FSaveMainFileAs.Enabled := FActiveMemo = FMainMemo;
   FSaveEncoding.Enabled := FSave.Enabled; { FSave.Enabled is kept up-to-date by UpdateSaveMenuItemAndButton }
   FSaveEncoding.Enabled := FSave.Enabled; { FSave.Enabled is kept up-to-date by UpdateSaveMenuItemAndButton }
-  FSaveEncodingAuto.Checked := FSaveEncoding.Enabled and not (FActiveMemo as TCompScintFileEdit).SaveInUTF8Encoding;
-  FSaveEncodingUTF8.Checked := FSaveEncoding.Enabled and (FActiveMemo as TCompScintFileEdit).SaveInUTF8Encoding;
+  FSaveEncodingAuto.Checked := FSaveEncoding.Enabled and ((FActiveMemo as TCompScintFileEdit).SaveEncoding = seAuto);
+  FSaveEncodingUTF8.Checked := FSaveEncoding.Enabled and ((FActiveMemo as TCompScintFileEdit).SaveEncoding = seUTF8);
+  FSaveEncodingUTF8NoPreamble.Checked := FSaveEncoding.Enabled and ((FActiveMemo as TCompScintFileEdit).SaveEncoding = seUTF8NoPreamble);
   FSaveAll.Visible := FOptions.OpenIncludedFiles;
   FSaveAll.Visible := FOptions.OpenIncludedFiles;
   ReadMRUMainFilesList;
   ReadMRUMainFilesList;
   FMRUMainFilesSep.Visible := FMRUMainFilesList.Count <> 0;
   FMRUMainFilesSep.Visible := FMRUMainFilesList.Count <> 0;
@@ -1785,7 +1804,12 @@ end;
 
 
 procedure TCompileForm.FSaveEncodingItemClick(Sender: TObject);
 procedure TCompileForm.FSaveEncodingItemClick(Sender: TObject);
 begin
 begin
-  (FActiveMemo as TCompScintFileEdit).SaveInUTF8Encoding := (Sender = FSaveEncodingUTF8);
+  if Sender = FSaveEncodingUTF8  then
+    (FActiveMemo as TCompScintFileEdit).SaveEncoding := seUTF8
+  else if Sender = FSaveEncodingUTF8NoPreamble  then
+    (FActiveMemo as TCompScintFileEdit).SaveEncoding := seUTF8NoPreamble
+  else
+    (FActiveMemo as TCompScintFileEdit).SaveEncoding := seAuto;
 end;
 end;
 
 
 procedure TCompileForm.FSaveAllClick(Sender: TObject);
 procedure TCompileForm.FSaveAllClick(Sender: TObject);

+ 8 - 6
Projects/CompFunc.pas

@@ -2,7 +2,7 @@ unit CompFunc;
 
 
 {
 {
   Inno Setup
   Inno Setup
-  Copyright (C) 1997-2020 Jordan Russell
+  Copyright (C) 1997-2024 Jordan Russell
   Portions by Martijn Laan
   Portions by Martijn Laan
   For conditions of distribution and use, see LICENSE.TXT.
   For conditions of distribution and use, see LICENSE.TXT.
 
 
@@ -16,7 +16,7 @@ interface
 uses
 uses
   Windows,
   Windows,
   Classes, Forms, Dialogs, Menus, StdCtrls,
   Classes, Forms, Dialogs, Menus, StdCtrls,
-  ScintEdit, ModernColors;
+  ScintEdit, CompScintEdit, ModernColors;
 
 
 const
 const
   MRUListMaxCount = 10;
   MRUListMaxCount = 10;
@@ -48,7 +48,7 @@ procedure SetFakeShortCutText(const MenuItem: TMenuItem; const S: String);
 procedure SetFakeShortCut(const MenuItem: TMenuItem; const Key: Word;
 procedure SetFakeShortCut(const MenuItem: TMenuItem; const Key: Word;
   const Shift: TShiftState);
   const Shift: TShiftState);
 procedure SaveTextToFile(const Filename: String;
 procedure SaveTextToFile(const Filename: String;
-  const S: String; const ForceUTF8Encoding: Boolean);
+  const S: String; const SaveEncoding: TSaveEncoding);
 procedure AddLines(const ListBox: TListBox; const S: String; const AObject: TObject; const LineBreaks: Boolean; const Prefix: TAddLinesPrefix; const PrefixParam: Cardinal);
 procedure AddLines(const ListBox: TListBox; const S: String; const AObject: TObject; const LineBreaks: Boolean; const Prefix: TAddLinesPrefix; const PrefixParam: Cardinal);
 procedure SetLowPriority(ALowPriority: Boolean; var SavePriorityClass: DWORD);
 procedure SetLowPriority(ALowPriority: Boolean; var SavePriorityClass: DWORD);
 function GetHelpFile: String;
 function GetHelpFile: String;
@@ -306,14 +306,14 @@ begin
 end;
 end;
 
 
 procedure SaveTextToFile(const Filename: String;
 procedure SaveTextToFile(const Filename: String;
-  const S: String; const ForceUTF8Encoding: Boolean);
+  const S: String; const SaveEncoding: TSaveEncoding);
 var
 var
   AnsiMode: Boolean;
   AnsiMode: Boolean;
   AnsiStr: AnsiString;
   AnsiStr: AnsiString;
   F: TTextFileWriter;
   F: TTextFileWriter;
 begin
 begin
   AnsiMode := False;
   AnsiMode := False;
-  if not ForceUTF8Encoding then begin
+  if SaveEncoding = seAuto then begin
     AnsiStr := AnsiString(S);
     AnsiStr := AnsiString(S);
     if S = String(AnsiStr) then
     if S = String(AnsiStr) then
       AnsiMode := True;
       AnsiMode := True;
@@ -323,8 +323,10 @@ begin
   try
   try
     if AnsiMode then
     if AnsiMode then
       F.WriteAnsi(AnsiStr)
       F.WriteAnsi(AnsiStr)
-    else
+    else begin
+      F.UTF8NoPreamble := SaveEncoding = seUTF8NoPreamble;
       F.Write(S);
       F.Write(S);
+    end;
   finally
   finally
     F.Free;
     F.Free;
   end;
   end;

+ 4 - 3
Projects/CompScintEdit.pas

@@ -2,7 +2,7 @@ unit CompScintEdit;
 
 
 {
 {
   Inno Setup
   Inno Setup
-  Copyright (C) 1997-2020 Jordan Russell
+  Copyright (C) 1997-2024 Jordan Russell
   Portions by Martijn Laan
   Portions by Martijn Laan
   For conditions of distribution and use, see LICENSE.TXT.
   For conditions of distribution and use, see LICENSE.TXT.
 
 
@@ -37,6 +37,7 @@ type
   TLineState = (lnUnknown, lnHasEntry, lnEntryProcessed);
   TLineState = (lnUnknown, lnHasEntry, lnEntryProcessed);
   PLineStateArray = ^TLineStateArray;
   PLineStateArray = ^TLineStateArray;
   TLineStateArray = array[0..0] of TLineState;
   TLineStateArray = array[0..0] of TLineState;
+  TSaveEncoding = (seAuto, seUTF8, seUTF8NoPreamble);
 
 
   TCompScintEdit = class(TScintEdit)
   TCompScintEdit = class(TScintEdit)
   private
   private
@@ -56,7 +57,7 @@ type
     FCompilerFileIndex: Integer;
     FCompilerFileIndex: Integer;
     FFilename: String;
     FFilename: String;
     FFileLastWriteTime: TFileTime;
     FFileLastWriteTime: TFileTime;
-    FSaveInUTF8Encoding: Boolean;
+    FSaveEncoding: TSaveEncoding;
   public
   public
     ErrorLine, ErrorCaretPosition: Integer;
     ErrorLine, ErrorCaretPosition: Integer;
     StepLine: Integer;
     StepLine: Integer;
@@ -68,7 +69,7 @@ type
     property Filename: String read FFileName write FFilename;
     property Filename: String read FFileName write FFilename;
     property CompilerFileIndex: Integer read FCompilerFileIndex write FCompilerFileIndex;
     property CompilerFileIndex: Integer read FCompilerFileIndex write FCompilerFileIndex;
     property FileLastWriteTime: TFileTime read FFileLastWriteTime write FFileLastWriteTime;
     property FileLastWriteTime: TFileTime read FFileLastWriteTime write FFileLastWriteTime;
-    property SaveInUTF8Encoding: Boolean read FSaveInUTF8Encoding write FSaveInUTF8Encoding;
+    property SaveEncoding: TSaveEncoding read FSaveEncoding write FSaveEncoding;
   end;
   end;
 
 
 implementation
 implementation

+ 24 - 8
Projects/FileClass.pas

@@ -2,7 +2,7 @@ unit FileClass;
 
 
 {
 {
   Inno Setup
   Inno Setup
-  Copyright (C) 1997-2010 Jordan Russell
+  Copyright (C) 1997-2024 Jordan Russell
   Portions by Martijn Laan
   Portions by Martijn Laan
   For conditions of distribution and use, see LICENSE.TXT.
   For conditions of distribution and use, see LICENSE.TXT.
 
 
@@ -11,8 +11,6 @@ unit FileClass;
   and uses descriptive, localized system error messages.
   and uses descriptive, localized system error messages.
 
 
   TTextFileReader and TTextFileWriter support ANSI and UTF8 textfiles only.
   TTextFileReader and TTextFileWriter support ANSI and UTF8 textfiles only.
-
-  $jrsoftware: issrc/Projects/FileClass.pas,v 1.31 2010/01/26 06:26:18 jr Exp $
 }
 }
 
 
 {$I VERSION.INC}
 {$I VERSION.INC}
@@ -117,12 +115,14 @@ type
   TTextFileWriter = class(TFile)
   TTextFileWriter = class(TFile)
   private
   private
     FSeekedToEnd: Boolean;
     FSeekedToEnd: Boolean;
+    FUTF8NoPreamble: Boolean;
     procedure DoWrite(const S: AnsiString{$IFDEF UNICODE}; const UTF8: Boolean{$ENDIF});
     procedure DoWrite(const S: AnsiString{$IFDEF UNICODE}; const UTF8: Boolean{$ENDIF});
   protected
   protected
     function CreateHandle(const AFilename: String;
     function CreateHandle(const AFilename: String;
       ACreateDisposition: TFileCreateDisposition; AAccess: TFileAccess;
       ACreateDisposition: TFileCreateDisposition; AAccess: TFileAccess;
       ASharing: TFileSharing): THandle; override;
       ASharing: TFileSharing): THandle; override;
   public
   public
+    property UTF8NoPreamble: Boolean read FUTF8NoPreamble write FUTF8NoPreamble;
     procedure Write(const S: String);
     procedure Write(const S: String);
     procedure WriteLine(const S: String);
     procedure WriteLine(const S: String);
 {$IFDEF UNICODE}
 {$IFDEF UNICODE}
@@ -155,6 +155,7 @@ type
 implementation
 implementation
 
 
 uses
 uses
+  WideStrUtils,
   CmnFunc2;
   CmnFunc2;
 
 
 const
 const
@@ -503,10 +504,25 @@ begin
   end;
   end;
   {$IFDEF UNICODE}
   {$IFDEF UNICODE}
   if not FSawFirstLine then begin
   if not FSawFirstLine then begin
-    { Handle UTF8 BOM if requested }
-    if UTF8 and (Length(S) > 2) and (S[1] = #$EF) and (S[2] = #$BB) and (S[3] = #$BF) then begin
-      Delete(S, 1, 3);
-      FCodePage := CP_UTF8;
+    if UTF8 then begin
+      { Handle UTF8 as requested: check for a BOM at the start and if not found then check entire file }
+      if (Length(S) > 2) and (S[1] = #$EF) and (S[2] = #$BB) and (S[3] = #$BF) then begin
+        Delete(S, 1, 3);
+        FCodePage := CP_UTF8;
+      end else begin
+        var OldPosition := GetPosition;
+        try
+          var Size := CappedSize; //can't be 0
+          Seek(0);
+          var S2: AnsiString;
+          SetLength(S2, Size);
+          SetLength(S2, Read(S2[1], Size));
+          if IsUTF8String(S2) then
+            FCodePage := CP_UTF8;
+        finally
+          Seek64(OldPosition);
+        end;
+      end;
     end;
     end;
     FSawFirstLine := True;
     FSawFirstLine := True;
   end;
   end;
@@ -563,7 +579,7 @@ begin
         WriteBuffer(CRLF, SizeOf(CRLF));
         WriteBuffer(CRLF, SizeOf(CRLF));
       end;
       end;
 {$IFDEF UNICODE}
 {$IFDEF UNICODE}
-    end else if UTF8 then
+    end else if UTF8 and not FUTF8NoPreamble then
       WriteBuffer(UTF8Preamble, SizeOf(UTF8Preamble));
       WriteBuffer(UTF8Preamble, SizeOf(UTF8Preamble));
 {$ELSE}
 {$ELSE}
     end;
     end;

+ 2 - 1
Projects/ScriptFunc.pas

@@ -349,7 +349,7 @@ const
   );
   );
 
 
   { Other }
   { Other }
-  OtherTable: array [0..33] of AnsiString =
+  OtherTable: array [0..34] of AnsiString =
   (
   (
     'procedure BringToFrontAndRestore;',
     'procedure BringToFrontAndRestore;',
     'function WizardDirValue: String;',
     'function WizardDirValue: String;',
@@ -379,6 +379,7 @@ const
     'function SaveStringToFile(const FileName: String; const S: AnsiString; const Append: Boolean): Boolean;',
     'function SaveStringToFile(const FileName: String; const S: AnsiString; const Append: Boolean): Boolean;',
     'function SaveStringsToFile(const FileName: String; const S: TArrayOfString; const Append: Boolean): Boolean;',
     'function SaveStringsToFile(const FileName: String; const S: TArrayOfString; const Append: Boolean): Boolean;',
     'function SaveStringsToUTF8File(const FileName: String; const S: TArrayOfString; const Append: Boolean): Boolean;',
     'function SaveStringsToUTF8File(const FileName: String; const S: TArrayOfString; const Append: Boolean): Boolean;',
+    'function SaveStringsToUTF8FileNoPreamble(const FileName: String; const S: TArrayOfString; const Append: Boolean): Boolean;',
     'function EnableFsRedirection(const Enable: Boolean): Boolean;',
     'function EnableFsRedirection(const Enable: Boolean): Boolean;',
     'function GetUninstallProgressForm: TUninstallProgressForm;',
     'function GetUninstallProgressForm: TUninstallProgressForm;',
     'function CreateCallback(Method: AnyMethod): Longword;',
     'function CreateCallback(Method: AnyMethod): Longword;',

+ 8 - 3
Projects/ScriptFunc_R.pas

@@ -1793,7 +1793,7 @@ function OtherProc(Caller: TPSExec; Proc: TPSExternalProcRec; Global, Stack: TPS
     end;
     end;
   end;
   end;
 
 
-  function SaveStringsToFile(const FileName: String; const Arr: PPSVariantIFC; Append, UTF8: Boolean): Boolean;
+  function SaveStringsToFile(const FileName: String; const Arr: PPSVariantIFC; Append, UTF8, UTF8NoPreamble: Boolean): Boolean;
   var
   var
     F: TTextFileWriter;
     F: TTextFileWriter;
     I, N: Integer;
     I, N: Integer;
@@ -1809,6 +1809,8 @@ function OtherProc(Caller: TPSExec; Proc: TPSExternalProcRec; Global, Stack: TPS
       else
       else
         F := TTextFileWriterRedir.Create(ScriptFuncDisableFsRedir, FileName, fdCreateAlways, faWrite, fsNone);
         F := TTextFileWriterRedir.Create(ScriptFuncDisableFsRedir, FileName, fdCreateAlways, faWrite, fsNone);
       try
       try
+        if UTF8 and UTF8NoPreamble then
+          F.UTF8NoPreamble := UTF8NoPreamble;
         N := PSDynArrayGetLength(Pointer(Arr.Dta^), Arr.aType);
         N := PSDynArrayGetLength(Pointer(Arr.Dta^), Arr.aType);
         for I := 0 to N-1 do begin
         for I := 0 to N-1 do begin
           S := VNGetString(PSGetArrayField(Arr^, I));
           S := VNGetString(PSGetArrayField(Arr^, I));
@@ -2027,10 +2029,13 @@ begin
     Stack.SetBool(PStart, SaveStringToFile(Stack.GetString(PStart-1), StackGetAnsiString(Stack, PStart-2), Stack.GetBool(PStart-3)));
     Stack.SetBool(PStart, SaveStringToFile(Stack.GetString(PStart-1), StackGetAnsiString(Stack, PStart-2), Stack.GetBool(PStart-3)));
   end else if Proc.Name = 'SAVESTRINGSTOFILE' then begin
   end else if Proc.Name = 'SAVESTRINGSTOFILE' then begin
     Arr := NewTPSVariantIFC(Stack[PStart-2], True);
     Arr := NewTPSVariantIFC(Stack[PStart-2], True);
-    Stack.SetBool(PStart, SaveStringsToFile(Stack.GetString(PStart-1), @Arr, Stack.GetBool(PStart-3), False));
+    Stack.SetBool(PStart, SaveStringsToFile(Stack.GetString(PStart-1), @Arr, Stack.GetBool(PStart-3), False, False));
   end else if Proc.Name = 'SAVESTRINGSTOUTF8FILE' then begin
   end else if Proc.Name = 'SAVESTRINGSTOUTF8FILE' then begin
     Arr := NewTPSVariantIFC(Stack[PStart-2], True);
     Arr := NewTPSVariantIFC(Stack[PStart-2], True);
-    Stack.SetBool(PStart, SaveStringsToFile(Stack.GetString(PStart-1), @Arr, Stack.GetBool(PStart-3), True));
+    Stack.SetBool(PStart, SaveStringsToFile(Stack.GetString(PStart-1), @Arr, Stack.GetBool(PStart-3), True, False));
+  end else if Proc.Name = 'SAVESTRINGSTOUTF8FILENOPREAMBLE' then begin
+    Arr := NewTPSVariantIFC(Stack[PStart-2], True);
+    Stack.SetBool(PStart, SaveStringsToFile(Stack.GetString(PStart-1), @Arr, Stack.GetBool(PStart-3), True, True));
   end else if Proc.Name = 'ENABLEFSREDIRECTION' then begin
   end else if Proc.Name = 'ENABLEFSREDIRECTION' then begin
     Stack.SetBool(PStart, not ScriptFuncDisableFsRedir);
     Stack.SetBool(PStart, not ScriptFuncDisableFsRedir);
     if Stack.GetBool(PStart-1) then
     if Stack.GetBool(PStart-1) then

+ 12 - 0
whatsnew.htm

@@ -30,6 +30,18 @@ For conditions of distribution and use, see <a href="https://jrsoftware.org/file
 
 
 <p><a name="6.2.3"></a><span class="ver">6.2.3-dev </span><span class="date">(?)</span></p>
 <p><a name="6.2.3"></a><span class="ver">6.2.3-dev </span><span class="date">(?)</span></p>
 <ul>
 <ul>
+  <li>Inno Setup now supports UTF-8 encoded .iss and .isl files without an UTF-8 preamble (also called BOM.)</li>
+  <li>Compiler IDE changes:
+  <ul>
+  <li>Added new <i>UTF-8 without BOM</i> menu item to the <i>Save Encoding</i> submenu of the <i>File</i> menu.</li>
+  <li>New files are now saved as UTF-8 with BOM by default. Existing files are still saved as they were.</li>
+  </ul>
+  </li>
+  <li>Pascal Scripting changes:
+  <ul>
+    <li>Support fuction <tt>LoadStringsFromFile</tt> now also supports UT8-encoded files without an UTF-8 preamble.</li>
+    <li>Added new <tt>SaveStringsToUTF8FileNoPreamble</tt> support function.</li>
+  </ul>
   <li>During startup Setup would always ask Windows to create any missing <tt>{usercf}</tt>, <tt>{userpf}</tt>, and <tt>{usersavedgames}</tt> folders. It no longer does until the script asks for the folder. Note that scripts running in administrative install mode should not do this because it violates the <a href="https://jrsoftware.org/ishelp/index.php?topic=setup_useduserareaswarning">used user areas warning</a>.</li>
   <li>During startup Setup would always ask Windows to create any missing <tt>{usercf}</tt>, <tt>{userpf}</tt>, and <tt>{usersavedgames}</tt> folders. It no longer does until the script asks for the folder. Note that scripts running in administrative install mode should not do this because it violates the <a href="https://jrsoftware.org/ishelp/index.php?topic=setup_useduserareaswarning">used user areas warning</a>.</li>
   <li>Added support for IIS group users identifiers (<tt>iisiusrs</tt>) for use in <tt>Permissions</tt> parameters, contributed by Achim Stuy.</li> 
   <li>Added support for IIS group users identifiers (<tt>iisiusrs</tt>) for use in <tt>Permissions</tt> parameters, contributed by Achim Stuy.</li> 
   <li>Pascal Scripting change: type <tt>TShellFolderID</tt> was removed because it wasn't used by any support function.</a>
   <li>Pascal Scripting change: type <tt>TShellFolderID</tt> was removed because it wasn't used by any support function.</a>