From 0a2abc145a59bfb2b0e0402a86115fa2da53c681 Mon Sep 17 00:00:00 2001 From: tobigun Date: Fri, 15 Aug 2008 18:40:22 +0000 Subject: - Support for multiple song/cover paths. - Add multiple Song-Paths to the Ini file: [Directories] SongDir1=C:\... SongDir2=D:\... SongDirA=D:\... - UIni.pas clean-up git-svn-id: svn://svn.code.sf.net/p/ultrastardx/svn/trunk@1266 b956fd51-792f-4845-bead-9b4dfca2ff2c --- Game/Code/Classes/UCatCovers.pas | 40 +- Game/Code/Classes/UIni.pas | 587 +++++++++++++++------------ Game/Code/Classes/UMain.pas | 161 +++++--- Game/Code/Classes/USongs.pas | 8 +- Game/Code/Menu/UDisplay.pas | 4 + Game/Code/Screens/UScreenOptionsAdvanced.pas | 2 +- 6 files changed, 470 insertions(+), 332 deletions(-) (limited to 'Game/Code') diff --git a/Game/Code/Classes/UCatCovers.pas b/Game/Code/Classes/UCatCovers.pas index 8e7a2f62..d8f6cdb0 100644 --- a/Game/Code/Classes/UCatCovers.pas +++ b/Game/Code/Classes/UCatCovers.pas @@ -13,11 +13,12 @@ uses UIni; type TCatCovers = class protected - cNames: array [low(ISorting)..high(ISorting)] of array of string; - cFiles: array [low(ISorting)..high(ISorting)] of array of string; + cNames: array [0..high(ISorting)] of array of string; + cFiles: array [0..high(ISorting)] of array of string; public constructor Create; procedure Load; //Load Cover aus Cover.ini and Cover Folder + procedure LoadPath(const CoversPath: string); procedure Add(Sorting: integer; Name, Filename: string); //Add a Cover function CoverExists(Sorting: integer; Name: string): boolean; //Returns True when a cover with the given Name exists function GetCover(Sorting: integer; Name: string): string; //Returns the Filename of a Cover @@ -42,8 +43,18 @@ begin Load; end; - //Load Cover aus Cover.ini and Cover Folder procedure TCatCovers.Load; +var + I: integer; +begin + for I := 0 to CoverPaths.Count-1 do + LoadPath(CoverPaths[I]); +end; + +(** + * Load Cover from Cover.ini and Cover Folder + *) +procedure TCatCovers.LoadPath(const CoversPath: string); var Ini: TMemIniFile; SR: TSearchRec; @@ -59,7 +70,7 @@ begin List := TStringlist.Create; //Add every Cover in Covers Ini for Every Sorting option - for I := low(ISorting) to high(ISorting) do + for I := 0 to High(ISorting) do begin Ini.ReadSection(ISorting[I], List); @@ -80,7 +91,7 @@ begin Filename := CoversPath + Name; Delete (Name, length(Name) - 3, 4); - for I := low(ISorting) to high(ISorting) do + for I := 0 to high(ISorting) do begin Temp := Name; if ((I = sTitle) or (I = sTitle2)) and (Pos ('Title', Temp) <> 0) then @@ -118,7 +129,7 @@ begin Result := False; Name := Uppercase(Name); //Case Insensitiv - for I := low(cNames[Sorting]) to high(cNames[Sorting]) do + for I := 0 to high(cNames[Sorting]) do begin if (cNames[Sorting][I] = Name) then //Found Name begin @@ -131,12 +142,12 @@ end; //Returns the Filename of a Cover function TCatCovers.GetCover(Sorting: integer; Name: string): string; var -I: Integer; + I: Integer; begin Result := ''; Name := Uppercase(Name); - for I := low(cNames[Sorting]) to high(cNames[Sorting]) do + for I := 0 to high(cNames[Sorting]) do begin if cNames[Sorting][I] = Name then begin @@ -146,8 +157,17 @@ begin end; //No Cover - if (Result = '') AND (FileExists(CoversPath + 'NoCover.jpg')) then - Result := CoversPath + 'NoCover.jpg'; + if (Result = '') then + begin + for I := 0 to CoverPaths.Count-1 do + begin + if (FileExists(CoverPaths[I] + 'NoCover.jpg')) then + begin + Result := CoverPaths[I] + 'NoCover.jpg'; + Break; + end; + end; + end; end; end. diff --git a/Game/Code/Classes/UIni.pas b/Game/Code/Classes/UIni.pas index 64fa2da3..c7160ac0 100644 --- a/Game/Code/Classes/UIni.pas +++ b/Game/Code/Classes/UIni.pas @@ -40,10 +40,19 @@ type type TIni = class private - function ExtractKeyIndex(const key, prefix, suffix: string): integer; - function GetMaxKeyIndex(keys: TStringList; const prefix, suffix: string): integer; + function RemoveFileExt(FullName: string): string; + function ExtractKeyIndex(const Key, Prefix, Suffix: string): integer; + function GetMaxKeyIndex(Keys: TStringList; const Prefix, Suffix: string): integer; + function GetArrayIndex(const SearchArray: array of string; Value: string; CaseInsensitiv: Boolean = False): integer; + function ReadArrayIndex(const SearchArray: array of string; IniFile: TCustomIniFile; + IniSection: string; IniProperty: string; Default: integer): integer; + procedure LoadInputDeviceCfg(IniFile: TMemIniFile); procedure SaveInputDeviceCfg(IniFile: TIniFile); + procedure LoadThemes(IniFile: TCustomIniFile); + procedure LoadPaths(IniFile: TCustomIniFile); + procedure LoadScreenModes(IniFile: TCustomIniFile); + public Name: array[0..11] of string; @@ -106,7 +115,7 @@ type LoadAnimation: integer; EffectSing: integer; ScreenFade: integer; - AskbeforeDel: integer; + AskBeforeDel: integer; OnSongClick: integer; LineBonus: integer; PartyPopup: integer; @@ -114,12 +123,6 @@ type // Controller Joypad: integer; - // Soundcards - SoundCard: array[0..7, 1..2] of integer; - - // Devices - LPT: integer; - procedure Load(); procedure Save(); procedure SaveNames; @@ -209,7 +212,6 @@ const IPartyPopup: array[0..1] of string = ('Off', 'On'); IJoypad: array[0..1] of string = ('Off', 'On'); - ILPT: array[0..2] of string = ('Off', 'LCD', 'Lights'); // Recording options IChannelPlayer: array[0..6] of string = ('Off', '1', '2', '3', '4', '5', '6'); @@ -227,64 +229,120 @@ uses URecord, UCommandLine; -function TIni.ExtractKeyIndex(const key, prefix, suffix: string): integer; +(** + * Returns the filename without its fileextension + *) +function TIni.RemoveFileExt(FullName: string): string; +begin + Result := ChangeFileExt(FullName, ''); +end; + +(** + * Extracts an index of a key that is surrounded by a Prefix/Suffix pair. + * Example: ExtractKeyIndex('MyKey[1]', '[', ']') will return 1. + *) +function TIni.ExtractKeyIndex(const Key, Prefix, Suffix: string): integer; var - value: string; - start: integer; + Value: string; + Start: integer; begin Result := -1; - - if Pos(prefix, key) > -1 then + + if Pos(Prefix, Key) > -1 then begin - start := Pos(prefix, key) + Length(prefix); - + Start := Pos(Prefix, Key) + Length(Prefix); + // copy all between prefix and suffix - value := copy (key, start, Pos(suffix, key) - 1 - start); - Result := StrToIntDef(value, -1); + Value := Copy(Key, Start, Pos(Suffix, Key)-1 - Start); + Result := StrToIntDef(Value, -1); end; end; -function TIni.GetMaxKeyIndex(keys: TStringList; const prefix, suffix: string): integer; +(** + * Finds the maximum key-index in a key-list. + * The indexes of the list are surrounded by Prefix/Suffix, + * e.g. MyKey[1] (Prefix='[', Suffix=']') + *) +function TIni.GetMaxKeyIndex(Keys: TStringList; const Prefix, Suffix: string): integer; var i: integer; - keyIndex: integer; + KeyIndex: integer; begin Result := -1; - - for i := 0 to keys.Count-1 do + + for i := 0 to Keys.Count-1 do begin - keyIndex := ExtractKeyIndex(keys[i], prefix, suffix); - if (keyIndex > Result) then - Result := keyIndex; + KeyIndex := ExtractKeyIndex(Keys[i], Prefix, Suffix); + if (KeyIndex > Result) then + Result := KeyIndex; end; end; +(** + * Returns the index of Value in SearchArray + * or -1 if Value is not in SearchArray. + *) +function TIni.GetArrayIndex(const SearchArray: array of string; Value: string; + CaseInsensitiv: Boolean = False): integer; +var + i: integer; +begin + Result := -1; + + for i := 0 to High(SearchArray) do + begin + if (SearchArray[i] = Value) or + (CaseInsensitiv and (UpperCase(SearchArray[i]) = UpperCase(Value))) then + begin + Result := i; + Break; + end; + end; +end; + +(** + * Reads the property IniSeaction:IniProperty from IniFile and + * finds its corresponding index in SearchArray. + * If SearchArray does not contain the property value, the default value is + * returned. + *) +function TIni.ReadArrayIndex(const SearchArray: array of string; IniFile: TCustomIniFile; + IniSection: string; IniProperty: string; Default: integer): integer; +var + StrValue: string; +begin + StrValue := IniFile.ReadString(IniSection, IniProperty, SearchArray[Default]); + Result := GetArrayIndex(SearchArray, StrValue); + if (Result = -1) then + begin + Result := Default; + end; +end; + + procedure TIni.LoadInputDeviceCfg(IniFile: TMemIniFile); var - deviceCfg: PInputDeviceConfig; - deviceIniIndex: integer; - deviceIniStr: string; - channelCount: integer; - channelIndex: integer; - recordKeys: TStringList; + DeviceCfg: PInputDeviceConfig; + DeviceIndex: integer; + ChannelCount: integer; + ChannelIndex: integer; + RecordKeys: TStringList; i: integer; begin - recordKeys := TStringList.Create(); + RecordKeys := TStringList.Create(); // read all record-keys for filtering - IniFile.ReadSection('Record', recordKeys); + IniFile.ReadSection('Record', RecordKeys); SetLength(InputDeviceConfig, 0); - for i := 0 to recordKeys.Count-1 do + for i := 0 to RecordKeys.Count-1 do begin // find next device-name - deviceIniIndex := ExtractKeyIndex(recordKeys[i], 'DeviceName[', ']'); - if (deviceIniIndex >= 0) then + DeviceIndex := ExtractKeyIndex(RecordKeys[i], 'DeviceName[', ']'); + if (DeviceIndex >= 0) then begin - deviceIniStr := IntToStr(deviceIniIndex); - - if not IniFile.ValueExists('Record', 'DeviceName['+deviceIniStr+']') then + if not IniFile.ValueExists('Record', Format('DeviceName[%d]', [DeviceIndex])) then break; // resize list @@ -294,28 +352,28 @@ begin // Note: All devices are appended to the list whether they exist or not. // Otherwise an external device's config will be lost if it is not // connected (e.g. singstar mics or USB-Audio devices). - deviceCfg := @InputDeviceConfig[High(InputDeviceConfig)]; - deviceCfg.Name := IniFile.ReadString('Record', 'DeviceName['+deviceIniStr+']', ''); - deviceCfg.Input := IniFile.Readinteger('Record', 'Input['+deviceIniStr+']', 0); + DeviceCfg := @InputDeviceConfig[High(InputDeviceConfig)]; + DeviceCfg.Name := IniFile.ReadString('Record', Format('DeviceName[%d]', [DeviceIndex]), ''); + DeviceCfg.Input := IniFile.ReadInteger('Record', Format('Input[%d]', [DeviceIndex]), 0); // find the largest channel-number of the current device in the ini-file - channelCount := GetMaxKeyIndex(recordKeys, 'Channel(', ')['+deviceIniStr+']'); - if (channelCount < 0) then - channelCount := 0; + ChannelCount := GetMaxKeyIndex(RecordKeys, 'Channel', Format('[%d]', [DeviceIndex])); + if (ChannelCount < 0) then + ChannelCount := 0; - SetLength(deviceCfg.ChannelToPlayerMap, channelCount); + SetLength(DeviceCfg.ChannelToPlayerMap, ChannelCount); // read channel-to-player mapping for every channel of the current device // or set non-configured channels to no player (=0). - for channelIndex := 0 to High(deviceCfg.ChannelToPlayerMap) do + for ChannelIndex := 0 to High(DeviceCfg.ChannelToPlayerMap) do begin - deviceCfg.ChannelToPlayerMap[channelIndex] := - IniFile.Readinteger('Record', 'Channel('+IntToStr(channelIndex+1)+')['+deviceIniStr+']', 0); + DeviceCfg.ChannelToPlayerMap[ChannelIndex] := + IniFile.ReadInteger('Record', Format('Channel%d[%d]', [ChannelIndex+1, DeviceIndex]), 0); end; end; end; - recordKeys.Free(); + RecordKeys.Free(); // MicBoost //MicBoost := GetArrayIndex(IMicBoost, IniFile.ReadString('Record', 'MicBoost', 'Off')); @@ -325,23 +383,23 @@ end; procedure TIni.SaveInputDeviceCfg(IniFile: TIniFile); var - deviceIndex: integer; - deviceIndexStr: string; - channelIndex: integer; + DeviceIndex: integer; + ChannelIndex: integer; begin - for deviceIndex := 0 to High(InputDeviceConfig) do + for DeviceIndex := 0 to High(InputDeviceConfig) do begin - deviceIndexStr := IntToStr(deviceIndex+1); - // DeviceName and DeviceInput - IniFile.WriteString('Record', 'DeviceName['+deviceIndexStr+']', InputDeviceConfig[deviceIndex].Name); - IniFile.WriteString('Record', 'Input['+deviceIndexStr+']', IntToStr(InputDeviceConfig[deviceIndex].Input)); + IniFile.WriteString('Record', Format('DeviceName[%d]', [DeviceIndex+1]), + InputDeviceConfig[DeviceIndex].Name); + IniFile.WriteInteger('Record', Format('Input[%d]', [DeviceIndex+1]), + InputDeviceConfig[DeviceIndex].Input); // Channel-to-Player Mapping - for channelIndex := 0 to High(InputDeviceConfig[deviceIndex].ChannelToPlayerMap) do + for ChannelIndex := 0 to High(InputDeviceConfig[DeviceIndex].ChannelToPlayerMap) do begin - IniFile.WriteString('Record', 'Channel('+IntToStr(channelIndex+1)+')['+deviceIndexStr+']', - IntToStr(InputDeviceConfig[deviceIndex].ChannelToPlayerMap[channelIndex])); + IniFile.WriteInteger('Record', + Format('Channel%d[%d]', [ChannelIndex+1, DeviceIndex+1]), + InputDeviceConfig[DeviceIndex].ChannelToPlayerMap[ChannelIndex]); end; end; @@ -351,50 +409,77 @@ begin //IniFile.WriteString('Record', 'Threshold', IThreshold[ThresholdIndex]); end; -procedure TIni.Load(); +procedure TIni.LoadPaths(IniFile: TCustomIniFile); var - IniFile: TMemIniFile; - ThemeIni: TMemIniFile; - ThemeName: string; - I: integer; - Modes: PPSDL_Rect; - searchResult: TSearchRec; - // returns name without fileextension + PathStrings: TStringList; + I: integer; +begin + PathStrings := TStringList.Create; + IniFile.ReadSection('Directories', PathStrings); - function GetFileName (S: string):string; + // Load song-paths + for I := 0 to PathStrings.Count-1 do begin - //Result := copy (S,0,StrRScan (PChar(S),char('.'))+1); - Result := copy (S,0,Pos ('.ini',S)-1); + if (AnsiStartsText('SongDir', PathStrings[I])) then + begin + AddSongPath(IniFile.ReadString('Directories', PathStrings[I], '')); + end; end; - // get index of value V in array a, returns -1 if value is not in array - function GetArrayIndex(const A: array of string; V: string; caseInsensitiv: Boolean = False): integer; - var - i: integer; - begin - Result := -1; + PathStrings.Free; +end; + +procedure TIni.LoadThemes(IniFile: TCustomIniFile); +var + SearchResult: TSearchRec; + ThemeIni: TMemIniFile; + ThemeName: string; + I: integer; +begin + // Theme + SetLength(ITheme, 0); + Log.LogStatus('Searching for Theme : ' + ThemePath + '*.ini', 'Theme'); - for i := 0 To High(A) do - if (A[i] = V) or (caseInsensitiv and (UpperCase(A[i]) = UpperCase(V))) then + FindFirst(ThemePath + '*.ini',faAnyFile, SearchResult); + Repeat + Log.LogStatus('Found Theme: ' + SearchResult.Name, 'Theme'); + + //Read Themename from Theme + ThemeIni := TMemIniFile.Create(SearchResult.Name); + ThemeName := UpperCase(ThemeIni.ReadString('Theme','Name', RemoveFileExt(SearchResult.Name))); + ThemeIni.Free; + + //Search for Skins for this Theme + for I := Low(Skin.Skin) to High(Skin.Skin) do + begin + if UpperCase(Skin.Skin[I].Theme) = ThemeName then begin - Result := i; + SetLength(ITheme, Length(ITheme)+1); + ITheme[High(ITheme)] := RemoveFileExt(SearchResult.Name); break; end; - end; + end; + until FindNext(SearchResult) <> 0; + FindClose(SearchResult); - // get index of IniSeaction:IniProperty in array SearchArray or return the default value - function ReadArrayIndex(const SearchArray: array of string; IniSection: string; IniProperty: string; Default: integer): integer; - var - StrValue: string; + // No Theme Found + if (Length(ITheme) = 0) then begin - StrValue := IniFile.ReadString('Sound', 'AudioOutputBufferSize', SearchArray[Default]); - Result := GetArrayIndex(SearchArray, StrValue); - if (Result = -1) then - begin - Result := Default; - end; + Log.CriticalError('Could not find any valid Themes.'); end; + Theme := GetArrayIndex(ITheme, IniFile.ReadString('Themes', 'Theme', 'DELUXE'), true); + if (Theme = -1) then + Theme := 0; + + // Skin + Skin.onThemeChange; + + SkinNo := GetArrayIndex(ISkin, IniFile.ReadString('Themes', 'Skin', ISkin[0])); +end; + +procedure TIni.LoadScreenModes(IniFile: TCustomIniFile); + // swap two strings procedure swap(var s1, s2: string); var @@ -405,53 +490,10 @@ var s2 := s3; end; +var + Modes: PPSDL_Rect; + I: integer; begin - GamePath := Platform.GetGameUserPath; - - Log.LogStatus( 'GamePath : ' +GamePath , '' ); - - if (Params.ConfigFile <> '') then - try - FileName := Params.ConfigFile; - except - FileName := GamePath + 'config.ini'; - end - else - FileName := GamePath + 'config.ini'; - - Log.LogStatus( 'Using config : ' + FileName , 'Ini'); - IniFile := TMemIniFile.Create( FileName ); - - // Name - for I := 0 to 11 do - Name[I] := IniFile.ReadString('Name', 'P'+IntToStr(I+1), 'Player'+IntToStr(I+1)); - - // Templates for Names Mod - for I := 0 to 2 do - NameTeam[I] := IniFile.ReadString('NameTeam', 'T'+IntToStr(I+1), 'Team'+IntToStr(I+1)); - for I := 0 to 11 do - NameTemplate[I] := IniFile.ReadString('NameTemplate', 'Name'+IntToStr(I+1), 'Template'+IntToStr(I+1)); - - // Players - Players := GetArrayIndex(IPlayers, IniFile.ReadString('Game', 'Players', IPlayers[0])); - - // Difficulty - Difficulty := GetArrayIndex(IDifficulty, IniFile.ReadString('Game', 'Difficulty', 'Easy')); - - // Language - Language := GetArrayIndex(ILanguage, IniFile.ReadString('Game', 'Language', 'English')); - //Language.ChangeLanguage(ILanguage[Language]); - - // Tabs - Tabs := GetArrayIndex(ITabs, IniFile.ReadString('Game', 'Tabs', ITabs[0])); - Tabs_at_startup := Tabs; //Tabs at Startup fix - - // Song Sorting - Sorting := GetArrayIndex(ISorting, IniFile.ReadString('Game', 'Sorting', ISorting[0])); - - // Debug - Debug := GetArrayIndex(IDebug, IniFile.ReadString('Game', 'Debug', IDebug[0])); - // Screens Screens := GetArrayIndex(IScreens, IniFile.ReadString('Graphics', 'Screens', IScreens[0])); @@ -538,7 +580,61 @@ begin // Depth Depth := GetArrayIndex(IDepth, IniFile.ReadString('Graphics', 'Depth', '32 bit')); +end; + +procedure TIni.Load(); +var + IniFile: TMemIniFile; + I: integer; +begin + GamePath := Platform.GetGameUserPath; + + Log.LogStatus( 'GamePath : ' +GamePath , '' ); + + if (Params.ConfigFile <> '') then + try + FileName := Params.ConfigFile; + except + FileName := GamePath + 'config.ini'; + end + else + FileName := GamePath + 'config.ini'; + + Log.LogStatus( 'Using config : ' + FileName , 'Ini'); + IniFile := TMemIniFile.Create( FileName ); + + // Name + for I := 0 to 11 do + Name[I] := IniFile.ReadString('Name', 'P'+IntToStr(I+1), 'Player'+IntToStr(I+1)); + + // Templates for Names Mod + for I := 0 to 2 do + NameTeam[I] := IniFile.ReadString('NameTeam', 'T'+IntToStr(I+1), 'Team'+IntToStr(I+1)); + for I := 0 to 11 do + NameTemplate[I] := IniFile.ReadString('NameTemplate', 'Name'+IntToStr(I+1), 'Template'+IntToStr(I+1)); + + // Players + Players := GetArrayIndex(IPlayers, IniFile.ReadString('Game', 'Players', IPlayers[0])); + + // Difficulty + Difficulty := GetArrayIndex(IDifficulty, IniFile.ReadString('Game', 'Difficulty', 'Easy')); + + // Language + Language := GetArrayIndex(ILanguage, IniFile.ReadString('Game', 'Language', 'English')); + //Language.ChangeLanguage(ILanguage[Language]); + + // Tabs + Tabs := GetArrayIndex(ITabs, IniFile.ReadString('Game', 'Tabs', ITabs[0])); + Tabs_at_startup := Tabs; //Tabs at Startup fix + + // Song Sorting + Sorting := GetArrayIndex(ISorting, IniFile.ReadString('Game', 'Sorting', ISorting[0])); + // Debug + Debug := GetArrayIndex(IDebug, IniFile.ReadString('Game', 'Debug', IDebug[0])); + + LoadScreenModes(IniFile); + // TextureSize TextureSize := GetArrayIndex(ITextureSize, IniFile.ReadString('Graphics', 'TextureSize', ITextureSize[1])); @@ -567,7 +663,7 @@ begin SavePlayback := GetArrayIndex(ISavePlayback, IniFile.ReadString('Sound', 'SavePlayback', ISavePlayback[0])); // AudioOutputBufferSize - AudioOutputBufferSizeIndex := ReadArrayIndex(IAudioOutputBufferSize, 'Sound', 'AudioOutputBufferSize', 0); + AudioOutputBufferSizeIndex := ReadArrayIndex(IAudioOutputBufferSize, IniFile, 'Sound', 'AudioOutputBufferSize', 0); //Preview Volume PreviewVolume := GetArrayIndex(IPreviewVolume, IniFile.ReadString('Sound', 'PreviewVolume', IPreviewVolume[7])); @@ -590,45 +686,7 @@ begin // NoteLines NoteLines := GetArrayIndex(INoteLines, IniFile.ReadString('Lyrics', 'NoteLines', INoteLines[1])); - // Theme - SetLength(ITheme, 0); - Log.LogStatus('Searching for Theme : ' + ThemePath + '*.ini', 'Theme'); - FindFirst(ThemePath + '*.ini',faAnyFile, searchResult); - Repeat - Log.LogStatus('Found Theme: ' + searchResult.Name, 'Theme'); - - //Read Themename from Theme - ThemeIni := TMemIniFile.Create(searchResult.Name); - ThemeName := UpperCase(ThemeIni.ReadString('Theme','Name', GetFileName(searchResult.Name))); - ThemeIni.Free; - - //Search for Skins for this Theme - for I := Low(Skin.Skin) to High(Skin.Skin) do - begin - if UpperCase(Skin.Skin[I].Theme) = ThemeName then - begin - SetLength(ITheme, Length(ITheme)+1); - ITheme[High(ITheme)] := GetFileName(searchResult.Name); - break; - end; - end; - until FindNext(searchResult) <> 0; - FindClose(searchResult); - - // No Theme Found - if (Length(ITheme) = 0) then - begin - Log.CriticalError('Could not find any valid Themes.'); - end; - - Theme := GetArrayIndex(ITheme, IniFile.ReadString('Themes', 'Theme', 'DELUXE'), true); - if (Theme = -1) then - Theme := 0; - - // Skin - Skin.onThemeChange; - - SkinNo := GetArrayIndex(ISkin, IniFile.ReadString('Themes', 'Skin', ISkin[0])); + LoadThemes(IniFile); // Color Color := GetArrayIndex(IColor, IniFile.ReadString('Themes', 'Color', IColor[0])); @@ -645,7 +703,7 @@ begin EffectSing := GetArrayIndex(IEffectSing, IniFile.ReadString('Advanced', 'EffectSing', 'On')); // AskbeforeDel - AskbeforeDel := GetArrayIndex(IAskbeforeDel, IniFile.ReadString('Advanced', 'AskbeforeDel', 'On')); + AskBeforeDel := GetArrayIndex(IAskbeforeDel, IniFile.ReadString('Advanced', 'AskbeforeDel', 'On')); // OnSongClick OnSongClick := GetArrayIndex(IOnSongClick, IniFile.ReadString('Advanced', 'OnSongClick', 'Sing')); @@ -659,14 +717,7 @@ begin // Joypad Joypad := GetArrayIndex(IJoypad, IniFile.ReadString('Controller', 'Joypad', IJoypad[0])); - // LCD - LPT := GetArrayIndex(ILPT, IniFile.ReadString('Devices', 'LPT', ILPT[0])); - - // SongPath - if (Params.SongPath <> '') then - SongPath := IncludeTrailingPathDelimiter(Params.SongPath) - else - SongPath := IncludeTrailingPathDelimiter(IniFile.ReadString('Path', 'Songs', SongPath)); + LoadPaths(IniFile); IniFile.Free; end; @@ -675,76 +726,79 @@ procedure TIni.Save; var IniFile: TIniFile; begin - if not (FileExists(Filename) and FileIsReadOnly(Filename)) then + if (FileExists(Filename) and FileIsReadOnly(Filename)) then begin + Log.LogError('Config-file is read-only', 'TIni.Save'); + Exit; + end; - IniFile := TIniFile.Create(Filename); + IniFile := TIniFile.Create(Filename); - // Players - IniFile.WriteString('Game', 'Players', IPlayers[Players]); + // Players + IniFile.WriteString('Game', 'Players', IPlayers[Players]); - // Difficulty - IniFile.WriteString('Game', 'Difficulty', IDifficulty[Difficulty]); + // Difficulty + IniFile.WriteString('Game', 'Difficulty', IDifficulty[Difficulty]); - // Language - IniFile.WriteString('Game', 'Language', ILanguage[Language]); + // Language + IniFile.WriteString('Game', 'Language', ILanguage[Language]); - // Tabs - IniFile.WriteString('Game', 'Tabs', ITabs[Tabs]); + // Tabs + IniFile.WriteString('Game', 'Tabs', ITabs[Tabs]); - // Sorting - IniFile.WriteString('Game', 'Sorting', ISorting[Sorting]); + // Sorting + IniFile.WriteString('Game', 'Sorting', ISorting[Sorting]); - // Debug - IniFile.WriteString('Game', 'Debug', IDebug[Debug]); + // Debug + IniFile.WriteString('Game', 'Debug', IDebug[Debug]); - // Screens - IniFile.WriteString('Graphics', 'Screens', IScreens[Screens]); + // Screens + IniFile.WriteString('Graphics', 'Screens', IScreens[Screens]); - // FullScreen - IniFile.WriteString('Graphics', 'FullScreen', IFullScreen[FullScreen]); + // FullScreen + IniFile.WriteString('Graphics', 'FullScreen', IFullScreen[FullScreen]); - // Resolution - IniFile.WriteString('Graphics', 'Resolution', IResolution[Resolution]); + // Resolution + IniFile.WriteString('Graphics', 'Resolution', IResolution[Resolution]); - // Depth - IniFile.WriteString('Graphics', 'Depth', IDepth[Depth]); + // Depth + IniFile.WriteString('Graphics', 'Depth', IDepth[Depth]); - // TextureSize - IniFile.WriteString('Graphics', 'TextureSize', ITextureSize[TextureSize]); + // TextureSize + IniFile.WriteString('Graphics', 'TextureSize', ITextureSize[TextureSize]); - // Sing Window - IniFile.WriteString('Graphics', 'SingWindow', ISingWindow[SingWindow]); + // Sing Window + IniFile.WriteString('Graphics', 'SingWindow', ISingWindow[SingWindow]); - // Oscilloscope - IniFile.WriteString('Graphics', 'Oscilloscope', IOscilloscope[Oscilloscope]); + // Oscilloscope + IniFile.WriteString('Graphics', 'Oscilloscope', IOscilloscope[Oscilloscope]); - // Spectrum - IniFile.WriteString('Graphics', 'Spectrum', ISpectrum[Spectrum]); + // Spectrum + IniFile.WriteString('Graphics', 'Spectrum', ISpectrum[Spectrum]); - // Spectrograph - IniFile.WriteString('Graphics', 'Spectrograph', ISpectrograph[Spectrograph]); + // Spectrograph + IniFile.WriteString('Graphics', 'Spectrograph', ISpectrograph[Spectrograph]); - // Movie Size - IniFile.WriteString('Graphics', 'MovieSize', IMovieSize[MovieSize]); + // Movie Size + IniFile.WriteString('Graphics', 'MovieSize', IMovieSize[MovieSize]); - // ClickAssist - IniFile.WriteString('Sound', 'ClickAssist', IClickAssist[ClickAssist]); + // ClickAssist + IniFile.WriteString('Sound', 'ClickAssist', IClickAssist[ClickAssist]); - // BeatClick - IniFile.WriteString('Sound', 'BeatClick', IBeatClick[BeatClick]); + // BeatClick + IniFile.WriteString('Sound', 'BeatClick', IBeatClick[BeatClick]); - // AudioOutputBufferSize - IniFile.WriteString('Sound', 'AudioOutputBufferSize', IAudioOutputBufferSize[AudioOutputBufferSizeIndex]); + // AudioOutputBufferSize + IniFile.WriteString('Sound', 'AudioOutputBufferSize', IAudioOutputBufferSize[AudioOutputBufferSizeIndex]); - // Song Preview - IniFile.WriteString('Sound', 'PreviewVolume', IPreviewVolume[PreviewVolume]); + // Song Preview + IniFile.WriteString('Sound', 'PreviewVolume', IPreviewVolume[PreviewVolume]); - // PreviewFading - IniFile.WriteString('Sound', 'PreviewFading', IPreviewFading[PreviewFading]); + // PreviewFading + IniFile.WriteString('Sound', 'PreviewFading', IPreviewFading[PreviewFading]); - // SavePlayback - IniFile.WriteString('Sound', 'SavePlayback', ISavePlayback[SavePlayback]); + // SavePlayback + IniFile.WriteString('Sound', 'SavePlayback', ISavePlayback[SavePlayback]); // NoteLines IniFile.WriteString('Sound', 'VoicePassthrough', IVoicePassthrough[VoicePassthrough]); @@ -752,54 +806,55 @@ begin // Lyrics Font IniFile.WriteString('Lyrics', 'LyricsFont', ILyricsFont[LyricsFont]); - // Lyrics Effect - IniFile.WriteString('Lyrics', 'LyricsEffect', ILyricsEffect[LyricsEffect]); + // Lyrics Effect + IniFile.WriteString('Lyrics', 'LyricsEffect', ILyricsEffect[LyricsEffect]); - // Solmization - IniFile.WriteString('Lyrics', 'Solmization', ISolmization[Solmization]); + // Solmization + IniFile.WriteString('Lyrics', 'Solmization', ISolmization[Solmization]); - // NoteLines - IniFile.WriteString('Lyrics', 'NoteLines', INoteLines[NoteLines]); + // NoteLines + IniFile.WriteString('Lyrics', 'NoteLines', INoteLines[NoteLines]); - // Theme - IniFile.WriteString('Themes', 'Theme', ITheme[Theme]); + // Theme + IniFile.WriteString('Themes', 'Theme', ITheme[Theme]); - // Skin - IniFile.WriteString('Themes', 'Skin', ISkin[SkinNo]); + // Skin + IniFile.WriteString('Themes', 'Skin', ISkin[SkinNo]); - // Color - IniFile.WriteString('Themes', 'Color', IColor[Color]); + // Color + IniFile.WriteString('Themes', 'Color', IColor[Color]); - SaveInputDeviceCfg(IniFile); - //Log.LogError(InttoStr(Length(CardList)) + ' Cards Saved'); + SaveInputDeviceCfg(IniFile); - //LoadAnimation - IniFile.WriteString('Advanced', 'LoadAnimation', ILoadAnimation[LoadAnimation]); + //LoadAnimation + IniFile.WriteString('Advanced', 'LoadAnimation', ILoadAnimation[LoadAnimation]); - //EffectSing - IniFile.WriteString('Advanced', 'EffectSing', IEffectSing[EffectSing]); + //EffectSing + IniFile.WriteString('Advanced', 'EffectSing', IEffectSing[EffectSing]); - //ScreenFade - IniFile.WriteString('Advanced', 'ScreenFade', IScreenFade[ScreenFade]); + //ScreenFade + IniFile.WriteString('Advanced', 'ScreenFade', IScreenFade[ScreenFade]); - //AskbeforeDel - IniFile.WriteString('Advanced', 'AskbeforeDel', IAskbeforeDel[AskbeforeDel]); + //AskbeforeDel + IniFile.WriteString('Advanced', 'AskbeforeDel', IAskbeforeDel[AskBeforeDel]); - //OnSongClick - IniFile.WriteString('Advanced', 'OnSongClick', IOnSongClick[OnSongClick]); + //OnSongClick + IniFile.WriteString('Advanced', 'OnSongClick', IOnSongClick[OnSongClick]); - //Line Bonus - IniFile.WriteString('Advanced', 'LineBonus', ILineBonus[LineBonus]); + //Line Bonus + IniFile.WriteString('Advanced', 'LineBonus', ILineBonus[LineBonus]); - //Party Popup - IniFile.WriteString('Advanced', 'PartyPopup', IPartyPopup[PartyPopup]); + //Party Popup + IniFile.WriteString('Advanced', 'PartyPopup', IPartyPopup[PartyPopup]); - // Joypad - IniFile.WriteString('Controller', 'Joypad', IJoypad[Joypad]); + // Joypad + IniFile.WriteString('Controller', 'Joypad', IJoypad[Joypad]); - IniFile.Free; + // Directories (add a template if section is missing) + if (not IniFile.SectionExists('Directories')) then + IniFile.WriteString('Directories', 'SongDir1', ''); - end; + IniFile.Free; end; procedure TIni.SaveNames; diff --git a/Game/Code/Classes/UMain.pas b/Game/Code/Classes/UMain.pas index d0c702b9..da9df6d3 100644 --- a/Game/Code/Classes/UMain.pas +++ b/Game/Code/Classes/UMain.pas @@ -9,11 +9,12 @@ interface {$I switches.inc} uses + SysUtils, + Classes, SDL, UMusic, URecord, UTime, - SysUtils, UDisplay, UIni, ULog, @@ -67,22 +68,18 @@ var // Absolute Paths GamePath: string; SoundPath: string; - SongPath: string; + SongPaths: TStringList; LogPath: string; ThemePath: string; SkinsPath: string; ScreenshotsPath: string; - CoversPath: string; + CoverPaths: TStringList; LanguagesPath: string; PluginPath: string; VisualsPath: string; ResourcesPath: string; PlayListPath: string; - UserSongPath: string = ''; - UserCoversPath: string = ''; - UserPlaylistPath: string = ''; - Done: Boolean; Event: TSDL_event; // FIXME: ConversionFileName should not be global @@ -99,7 +96,9 @@ const MAX_SONG_SCORE = 10000; // max. achievable points per song MAX_SONG_LINE_BONUS = 1000; // max. achievable line bonus per song +function FindPath(out PathResult: string; const RequestedPath: string; NeedsWritePermission: boolean): boolean; procedure InitializePaths; +procedure AddSongPath(const Path: string); Procedure Main; procedure MainLoop; @@ -116,9 +115,10 @@ procedure ClearScores(PlayerNum: integer); implementation uses + Math, + StrUtils, USongs, UJoystick, - math, UCommandLine, ULanguage, //SDL_ttf, @@ -989,60 +989,119 @@ begin end; end; -//-------------------- -// Function sets all absolute paths e.g. song path and makes sure the directorys exist -//-------------------- -procedure InitializePaths; +procedure AddSpecialPath(var PathList: TStringList; const Path: string); +var + I: integer; + PathAbs, OldPathAbs: string; +begin + if (PathList = nil) then + PathList := TStringList.Create; - // Initialize a path variable - // After setting paths, make sure that paths exist - {$WARN SYMBOL_PLATFORM OFF} - function initialize_path( out aPathVar : string; const aLocation : string ): boolean; - var - lWriteable: Boolean; - lAttrib : integer; - begin - lWriteable := false; - aPathVar := aLocation; + if (Path = '') or not DirectoryExists(Path) then + Exit; - // Make sure the directory is needex - ForceDirectories(aPathVar); + PathAbs := IncludeTrailingPathDelimiter(ExpandFileName(Path)); - if DirectoryExists(aPathVar) then + // check if path or a part of the path was already added + for I := 0 to PathList.Count-1 do + begin + OldPathAbs := IncludeTrailingPathDelimiter(ExpandFileName(PathList[I])); + // check if the new directory is a sub-directory of a previously added one. + // This is also true, if both paths point to the same directories. + if (AnsiStartsText(OldPathAbs, PathAbs)) then begin - lAttrib := fileGetAttr(aPathVar); + // ignore the new path + Exit; + end; - lWriteable := (lAttrib and faDirectory <> 0) and - not (lAttrib and faReadOnly <> 0) + // check if a previously added directory is a sub-directory of the new one. + if (AnsiStartsText(PathAbs, OldPathAbs)) then + begin + // replace the old with the new one. + PathList[I] := PathAbs; + Exit; end; + end; + + PathList.Add(PathAbs); +end; + +procedure AddSongPath(const Path: string); +begin + AddSpecialPath(SongPaths, Path); +end; + +procedure AddCoverPath(const Path: string); +begin + AddSpecialPath(CoverPaths, Path); +end; + +(** + * Initialize a path variable + * After setting paths, make sure that paths exist + *) +function FindPath(out PathResult: string; const RequestedPath: string; NeedsWritePermission: boolean): boolean; +begin + Result := false; - if not lWriteable then - Log.LogWarn('Dir ('+ aLocation +') is Readonly', 'initialize_path'); + if (RequestedPath = '') then + Exit; - result := lWriteable; + // Make sure the directory exists + if (not ForceDirectories(RequestedPath)) then + begin + PathResult := ''; + Exit; + end; + + PathResult := IncludeTrailingPathDelimiter(RequestedPath); + + if (NeedsWritePermission) and + (FileIsReadOnly(RequestedPath)) then + begin + Exit; end; - {$WARN SYMBOL_PLATFORM ON} + Result := true; +end; + +(** + * Function sets all absolute paths e.g. song path and makes sure the directorys exist + *) +procedure InitializePaths; begin - initialize_path( LogPath , Platform.GetLogPath ); - initialize_path( SoundPath , Platform.GetGameSharedPath + 'Sounds' + PathDelim ); - initialize_path( ThemePath , Platform.GetGameSharedPath + 'Themes' + PathDelim ); - initialize_path( SkinsPath , Platform.GetGameSharedPath + 'Themes' + PathDelim ); - initialize_path( LanguagesPath , Platform.GetGameSharedPath + 'Languages' + PathDelim ); - initialize_path( PluginPath , Platform.GetGameSharedPath + 'Plugins' + PathDelim ); - initialize_path( VisualsPath , Platform.GetGameSharedPath + 'Visuals' + PathDelim ); - initialize_path( ResourcesPath , Platform.GetGameSharedPath + 'Resources' + PathDelim ); - initialize_path( ScreenshotsPath , Platform.GetGameUserPath + 'Screenshots' + PathDelim ); - - // Users Song Path .... - initialize_path( UserSongPath , Platform.GetGameUserPath + 'Songs' + PathDelim ); - initialize_path( UserCoversPath , Platform.GetGameUserPath + 'Covers' + PathDelim ); - initialize_path( UserPlaylistPath , Platform.GetGameUserPath + 'Playlists' + PathDelim ); - - // Shared Song Path .... - initialize_path( SongPath , Platform.GetGameSharedPath + 'Songs' + PathDelim ); - initialize_path( CoversPath , Platform.GetGameSharedPath + 'Covers' + PathDelim ); - initialize_path( PlaylistPath , Platform.GetGameSharedPath + 'Playlists' + PathDelim ); + // Log directory (must be writable) + if (not FindPath(LogPath, Platform.GetLogPath, true)) then + begin + Log.FileOutputEnabled := false; + Log.LogWarn('Log directory "'+ Platform.GetLogPath +'" not available', 'InitializePaths'); + end; + + FindPath(SoundPath, Platform.GetGameSharedPath + 'Sounds', false); + FindPath(ThemePath, Platform.GetGameSharedPath + 'Themes', false); + FindPath(SkinsPath, Platform.GetGameSharedPath + 'Themes', false); + FindPath(LanguagesPath, Platform.GetGameSharedPath + 'Languages', false); + FindPath(PluginPath, Platform.GetGameSharedPath + 'Plugins', false); + FindPath(VisualsPath, Platform.GetGameSharedPath + 'Visuals', false); + FindPath(ResourcesPath, Platform.GetGameSharedPath + 'Resources', false); + + // Playlists are not shared as we need one directory to write too + FindPath(PlaylistPath, Platform.GetGameUserPath + 'Playlists', true); + + // Screenshot directory (must be writable) + if (not FindPath(ScreenshotsPath, Platform.GetGameUserPath + 'Screenshots', true)) then + begin + Log.LogWarn('Screenshot directory "'+ Platform.GetGameUserPath +'" not available', 'InitializePaths'); + end; + + // Add song paths + AddSongPath(Params.SongPath); + AddSongPath(Platform.GetGameSharedPath + 'Songs'); + AddSongPath(Platform.GetGameUserPath + 'Songs'); + + // Add category cover paths + AddCoverPath(Platform.GetGameSharedPath + 'Covers'); + AddCoverPath(Platform.GetGameUserPath + 'Covers'); end; end. diff --git a/Game/Code/Classes/USongs.pas b/Game/Code/Classes/USongs.pas index 2c8e3587..710cd44f 100644 --- a/Game/Code/Classes/USongs.pas +++ b/Game/Code/Classes/USongs.pas @@ -195,6 +195,8 @@ begin end; procedure TSongs.int_LoadSongList; +var + I: integer; begin try fProcessing := true; @@ -202,10 +204,8 @@ begin Log.LogStatus('Searching For Songs', 'SongList'); // browse directories - BrowseDir(SongPath); - - if UserSongPath <> SongPath then - BrowseDir(UserSongPath); + for I := 0 to SongPaths.Count-1 do + BrowseDir(SongPaths[I]); if assigned( CatSongs ) then CatSongs.Refresh; diff --git a/Game/Code/Menu/UDisplay.pas b/Game/Code/Menu/UDisplay.pas index 67eed740..2b10b2c6 100644 --- a/Game/Code/Menu/UDisplay.pas +++ b/Game/Code/Menu/UDisplay.pas @@ -290,6 +290,10 @@ var Align: integer; RowSize: integer; begin + // Exit if Screenshot-path does not exist or read-only + if (ScreenshotsPath = '') then + Exit; + for Num := 1 to 9999 do begin FileName := IntToStr(Num); diff --git a/Game/Code/Screens/UScreenOptionsAdvanced.pas b/Game/Code/Screens/UScreenOptionsAdvanced.pas index 711414aa..be8895e1 100644 --- a/Game/Code/Screens/UScreenOptionsAdvanced.pas +++ b/Game/Code/Screens/UScreenOptionsAdvanced.pas @@ -93,7 +93,7 @@ begin AddSelectSlide(Theme.OptionsAdvanced.SelectEffectSing, Ini.EffectSing, IEffectSing); AddSelectSlide(Theme.OptionsAdvanced.SelectLineBonus, Ini.LineBonus, ILineBonus); AddSelectSlide(Theme.OptionsAdvanced.SelectOnSongClick, Ini.OnSongClick, IOnSongClick); - AddSelectSlide(Theme.OptionsAdvanced.SelectAskbeforeDel, Ini.AskbeforeDel, IAskbeforeDel); + AddSelectSlide(Theme.OptionsAdvanced.SelectAskbeforeDel, Ini.AskBeforeDel, IAskbeforeDel); AddSelectSlide(Theme.OptionsAdvanced.SelectPartyPopup, Ini.PartyPopup, IPartyPopup); AddButton(Theme.OptionsAdvanced.ButtonExit); -- cgit v1.2.3