diff --git a/CUE4Parse b/CUE4Parse index ecc48789..cf74fc32 160000 --- a/CUE4Parse +++ b/CUE4Parse @@ -1 +1 @@ -Subproject commit ecc4878950336126f125af0747190edf474b2a21 +Subproject commit cf74fc32fe1b40e9fd3440032508c5e1d50cf58d diff --git a/FModel/Resources/Lua.xshd b/FModel/Resources/Lua.xshd index 148f5b97..88d749f2 100644 --- a/FModel/Resources/Lua.xshd +++ b/FModel/Resources/Lua.xshd @@ -1,58 +1,76 @@ - - - - - - - - - + + + + + + + + + + + + + + + --\[\[(.|\n)*?\]\] --.*$ + \[\[(.|\n)*?\]\] "([^"\\]|\\.)*" '([^'\\]|\\.)*' + \b0[xX][0-9A-Fa-f]+\b \b\d+\.\d+([eE][+-]?\d+)?\b \b\d+[eE][+-]?\d+\b \b\d+\b - return - function - goto - end - if + and + break + do else elseif - then + end for + function + goto + if in + or + repeat + return + then until while - break - or - and - repeat - do + false local nil not true - false + + + + self + _G + _ENV - assert collectgarbage + dofile error + getmetatable ipairs + load + loadfile + module next pairs pcall @@ -61,128 +79,24 @@ rawget rawlen rawset + require select setmetatable tonumber tostring type + unpack xpcall - getmetatable - require - module - - + bit32 + coroutine + debug + io math + os + package string table - coroutine - os - io utf8 - bit32 - package - debug - - - arshift - band - bnot - bor - bxor - btest - extract - lrotate - lshift - replace - rrotate - rshift - - - create - resume - running - status - wrap - yield - isyieldable - - - getuservalue - gethook - getinfo - getlocal - getregistry - getupvalue - upvaluejoin - upvalueid - setuservalue - sethook - setlocal - setupvalue - traceback - - - close - flush - input - lines - open - output - popen - read - tmpfile - seek - setvbuf - write - - - byte - char - dump - find - format - gmatch - gsub - len - lower - match - rep - reverse - sub - upper - pack - packsize - unpack - concat - maxn - insert - move - offset - codepoint - codes - charpattern - - - clock - date - difftime - execute - exit - getenv - remove - rename - setlocale - time - loadlib - searchpath - seeall - preload - cpath - path - searchers - loaded - - abs acos asin @@ -195,36 +109,140 @@ exp floor fmod - ult + frexp + huge + ldexp log log10 max + maxinteger min + mininteger modf pi + pow rad random randomseed sin + sinh sqrt tan - sinh tanh - pow - frexp - ldexp - huge - maxinteger - mininteger + tointeger + ult + byte + char + dump + find + format + gmatch + gsub + len + lower + match + pack + packsize + rep + reverse + sub + upper + concat + insert + maxn + move + remove + sort + close + flush + input + lines + open + output + popen + read + seek + setvbuf + tmpfile + write + clock + date + difftime + execute + exit + getenv + rename + setlocale + time + create + isyieldable + resume + running + status + wrap + yield + arshift + band + bnot + bor + btest + bxor + extract + lrotate + lshift + replace + rrotate + rshift + charpattern + codepoint + codes + offset + gethook + getinfo + getlocal + getregistry + getupvalue + getuservalue + sethook + setlocal + setupvalue + setuservalue + traceback + upvalueid + upvaluejoin + cpath + loaded + loadlib + path + preload + searchers + searchpath + seeall (\|)|(<<)|(>>)|(\/\/)|(==)|(~=)|(<=)|(>=)|(<)|(>)|(=)|(\()|(\))|(\{)|(\})|(\[)|(\])|(::)|(:)|(;)|(,)|(\.\.\.)|(\.\.)|(\.)|[+\-*%\^#&~] - (?<=function\s)[A-Za-z0-9_]+(?=\.) - - (?<=\.)[A-Za-z0-9_]+(?=\() - (?<=function\s)[A-Za-z0-9_]+(?=\s*\() + __[A-Za-z_][A-Za-z0-9_]*__ - \b[A-Z_][A-Z0-9_]*\b + (?<=::)[A-Za-z_][A-Za-z0-9_]*(?=::) + + (?<![A-Za-z0-9_])_(?![A-Za-z0-9_]) + + (?<=function\s)[A-Za-z_][A-Za-z0-9_]*(?=\.) + (?<=function\s)[A-Za-z_][A-Za-z0-9_]*(?=:) + + (?<=function\s)[A-Za-z_][A-Za-z0-9_]*(?=\s*\() + + [A-Za-z_][A-Za-z0-9_]*(?=\.) + [A-Za-z_][A-Za-z0-9_]*(?=:) + + (?<=:)[A-Za-z_][A-Za-z0-9_]*(?=\s*\() + (?<=\.)[A-Za-z_][A-Za-z0-9_]*(?=\s*\() + \b[A-Za-z_][A-Za-z0-9_]*(?=\s*\() + + (?<=\.)[A-Za-z_][A-Za-z0-9_]* + + \b[A-Z][A-Z0-9]*_[A-Z0-9_]*\b + \b[A-Z]{2}[A-Z0-9_]*\b diff --git a/FModel/ViewModels/AudioPlayerViewModel.cs b/FModel/ViewModels/AudioPlayerViewModel.cs index 81a52065..554d978a 100644 --- a/FModel/ViewModels/AudioPlayerViewModel.cs +++ b/FModel/ViewModels/AudioPlayerViewModel.cs @@ -570,139 +570,126 @@ public class AudioPlayerViewModel : ViewModel, ISource, IDisposable { if (SelectedAudioFile?.Data == null) return false; + if (SelectedAudioFile.Extension == "wav") + return false; - switch (SelectedAudioFile.Extension) - { - case "binka": - case "adpcm": - case "xvag": - case "opus": - case "wem": - case "at9": - case "raw": - { - if (TryConvert(out var wavFilePath)) - { - var newAudio = new AudioFile(SelectedAudioFile.Id, new FileInfo(wavFilePath)); - Replace(newAudio); - return true; - } + if (!TryConvert(SelectedAudioFile.FilePath, SelectedAudioFile.Data, SelectedAudioFile.Extension, out var convertedFilePath, true)) + return false; - return false; - } - case "adx": - case "hca": - return TryConvertCriware(); - case "rada": - { - if (TryDecode(SelectedAudioFile.Extension, out var rawFilePath)) - { - var newAudio = new AudioFile(SelectedAudioFile.Id, new FileInfo(rawFilePath)); - Replace(newAudio); - return true; - } - - return false; - } - } + var newAudio = new AudioFile(SelectedAudioFile.Id, new FileInfo(convertedFilePath)); + Replace(newAudio); return true; } - private bool TryConvertCriware() + public static bool TryConvert(string inputFilePath, byte[] inputFileData, string extension, out string wavFilePath, bool updateUi = false) { + wavFilePath = string.Empty; + + switch (extension.ToLowerInvariant()) + { + case "hca": + case "adx": + return TryConvertCriware(inputFilePath, inputFileData, extension, out wavFilePath); + case "rada": + return TryConvertRada(inputFilePath, inputFileData, extension, out wavFilePath, updateUi); + default: + { + var vgmStreamPath = TryGetVgmstreamPath(); + if (string.IsNullOrEmpty(vgmStreamPath)) + return false; + + var success = TryConvertToWav(inputFilePath, inputFileData, vgmStreamPath, true, out wavFilePath); + + if (!success) + { + Log.Error("Failed to convert {InputFilePath} to .wav format", Path.GetFileName(inputFilePath)); + + if (updateUi) + { + FLogger.Append(ELog.Error, () => + { + FLogger.Text("Failed to convert audio to .wav format. See: ", Constants.WHITE); + FLogger.Link("→ link ←", Constants.AUDIO_ISSUE_LINK, true); + }); + } + } + + return success; + } + } + } + + private static bool TryConvertCriware(string inputFilePath, byte[] inputFileData, string extension, out string wavFilePath) + { + wavFilePath = string.Empty; + try { - byte[] wavData = SelectedAudioFile.Extension switch + byte[] wavData = extension switch { - "hca" => HcaWaveStream.ConvertHcaToWav( - SelectedAudioFile.Data, - UserSettings.Default.CurrentDir.CriwareDecryptionKey), - "adx" => AdxDecoder.ConvertAdxToWav( - SelectedAudioFile.Data, - UserSettings.Default.CurrentDir.CriwareDecryptionKey), + "hca" => HcaWaveStream.ConvertHcaToWav(inputFileData, UserSettings.Default.CurrentDir.CriwareDecryptionKey), + "adx" => AdxDecoder.ConvertAdxToWav(inputFileData, UserSettings.Default.CurrentDir.CriwareDecryptionKey), _ => throw new NotSupportedException() }; - if (wavData.Length is 0) + // Fallback for ADX + if (wavData.Length == 0) { - if (TryConvert(out var wavFilePathFallback)) - { - var newAudioFallback = new AudioFile(SelectedAudioFile.Id, new FileInfo(wavFilePathFallback)); - Replace(newAudioFallback); - return true; - } + var vgmStreamPath = TryGetVgmstreamPath(); + if (string.IsNullOrEmpty(vgmStreamPath)) + return false; + + return TryConvertToWav(inputFilePath, inputFileData, vgmStreamPath, true, out wavFilePath); } - string wavFilePath = Path.Combine( - UserSettings.Default.AudioDirectory, - SelectedAudioFile.FilePath.TrimStart('/')); - wavFilePath = Path.ChangeExtension(wavFilePath, ".wav"); + wavFilePath = Path.ChangeExtension(inputFilePath, ".wav"); + + var directory = Path.GetDirectoryName(wavFilePath); + if (!string.IsNullOrEmpty(directory)) + Directory.CreateDirectory(directory); - Directory.CreateDirectory(Path.GetDirectoryName(wavFilePath)!); File.WriteAllBytes(wavFilePath, wavData); - var newAudio = new AudioFile(SelectedAudioFile.Id, new FileInfo(wavFilePath)); - Replace(newAudio); - return true; } catch (CriwareDecryptionException ex) { - FLogger.Append(ELog.Error, () => FLogger.Text($"Encrypted {SelectedAudioFile.Extension.ToUpper()}: {ex.Message}", Constants.WHITE, true)); - Log.Error($"Encrypted {SelectedAudioFile.Extension.ToUpper()}: {ex.Message}"); + FLogger.Append(ELog.Error, () => FLogger.Text($"Encrypted {extension.ToUpper()}: {ex.Message}", Constants.WHITE, true)); + Log.Error($"Encrypted {extension.ToUpper()}: {ex.Message}"); + return false; } catch (Exception ex) { - FLogger.Append(ELog.Error, () => FLogger.Text($"Failed to convert {SelectedAudioFile.Extension.ToUpper()}: {ex.Message}", Constants.WHITE, true)); - Log.Error($"Failed to convert {SelectedAudioFile.Extension.ToUpper()}: {ex.Message}"); + FLogger.Append(ELog.Error, () => FLogger.Text($"Failed to convert {extension.ToUpper()}: {ex.Message}", Constants.WHITE, true)); + Log.Error($"Failed to convert {extension.ToUpper()}: {ex.Message}"); + return false; } } - private bool TryConvert(out string wavFilePath) => TryConvert(SelectedAudioFile.FilePath, SelectedAudioFile.Data, out wavFilePath, true); - public static bool TryConvert(string inputFilePath, byte[] inputFileData, out string wavFilePath, bool updateUi = false) - { - wavFilePath = string.Empty; - var vgmStreamPath = TryGetVgmstreamPath(); - if (string.IsNullOrEmpty(vgmStreamPath)) - return false; - - var success = TryConvertToWav(inputFilePath, inputFileData, vgmStreamPath, true, out wavFilePath); - - if (!success) - { - Log.Error("Failed to convert {InputFilePath} to .wav format", Path.GetFileName(inputFilePath)); - if (updateUi) - { - FLogger.Append(ELog.Error, () => - { - FLogger.Text("Failed to convert audio to .wav format. See: ", Constants.WHITE); - FLogger.Link("→ link ←", Constants.AUDIO_ISSUE_LINK, true); - }); - } - } - - return success; - } - - private bool TryDecode(string extension, out string rawFilePath) + private static bool TryConvertRada(string inputFilePath, byte[] inputFileData, string extension, out string rawFilePath, bool updateUi = false) { rawFilePath = string.Empty; var decoderPath = Path.Combine(UserSettings.Default.OutputDirectory, ".data", $"{extension}dec.exe"); if (!File.Exists(decoderPath)) { - Log.Error("Failed to convert {FilePath}, rada decoder is missing", SelectedAudioFile.FilePath); - FLogger.Append(ELog.Error, () => + Log.Error("Failed to convert {FilePath}, {Extension} decoder is missing", inputFilePath, extension); + + if (updateUi) { - FLogger.Text("Failed to convert audio because rada decoder is missing. See: ", Constants.WHITE); - FLogger.Link("→ link ←", Constants.RADA_ISSUE_LINK, true); - }); + FLogger.Append(ELog.Error, () => + { + FLogger.Text($"Failed to convert audio because {extension} decoder is missing. See: ", Constants.WHITE); + FLogger.Link("→ link ←", Constants.RADA_ISSUE_LINK, true); + }); + } + return false; } - return TryConvertToWav(SelectedAudioFile.FilePath, SelectedAudioFile.Data, decoderPath, false, out rawFilePath); + return TryConvertToWav(inputFilePath, inputFileData, decoderPath, false, out rawFilePath); } private static bool TryConvertToWav(string inputFilePath, byte[] inputFileData, string converterPath, bool usevgmstream, out string wavFilePath) diff --git a/FModel/ViewModels/CUE4ParseViewModel.cs b/FModel/ViewModels/CUE4ParseViewModel.cs index 5b97cc85..b30322cf 100644 --- a/FModel/ViewModels/CUE4ParseViewModel.cs +++ b/FModel/ViewModels/CUE4ParseViewModel.cs @@ -733,6 +733,7 @@ public class CUE4ParseViewModel : ViewModel case "archive": case "dnearchive": // Banishers: Ghosts of New Eden case "gitignore": + case "gitattributes": case "LICENSE": case "playstats": // Dispatch case "template": @@ -791,6 +792,8 @@ public class CUE4ParseViewModel : ViewModel case "bl": case "bm": case "br": + case "sql": + case "cs": { var data = Provider.SaveAsset(entry); using var stream = new MemoryStream(data) { Position = 0 }; @@ -1321,8 +1324,7 @@ public class CUE4ParseViewModel : ViewModel _ => [] }; - var directory = Path.GetDirectoryName(atomObject.Owner?.Name) ?? "/Criware/"; - directory = Path.GetDirectoryName(atomObject.Owner.Provider.FixPath(directory)); + var directory = Path.GetDirectoryName(Provider.FixPath(atomObject.Owner?.Name ?? "/Criware/")); foreach (var sound in extractedSounds) { SaveAndPlaySound(cancellationToken, Path.Combine(directory, sound.Name).Replace("\\", "/"), sound.Extension, sound.Data, saveAudio, updateUi); @@ -1616,7 +1618,7 @@ public class CUE4ParseViewModel : ViewModel bool conversionSuccess = true; if (UserSettings.Default.ConvertAudioOnBulkExport && extLower is not "wav") { - if (AudioPlayerViewModel.TryConvert(savedAudioPath, data, out string wavFilePath)) + if (AudioPlayerViewModel.TryConvert(savedAudioPath, data, extLower, out string wavFilePath)) savedAudioPath = wavFilePath; else { diff --git a/FModel/ViewModels/GameFileViewModel.cs b/FModel/ViewModels/GameFileViewModel.cs index 8f336ece..d5b90206 100644 --- a/FModel/ViewModels/GameFileViewModel.cs +++ b/FModel/ViewModels/GameFileViewModel.cs @@ -350,11 +350,15 @@ public class GameFileViewModel(GameFile asset) : ViewModel case "pem": case "xml": case "gitignore": + case "gitattributes": case "html": case "css": case "js": case "data": case "csv": + case "sql": + case "py": + case "cs": AssetCategory = EAssetCategory.Data; break; case "stinfo": diff --git a/FModel/Views/Resources/Colors.xaml b/FModel/Views/Resources/Colors.xaml index eb034871..10e746c7 100644 --- a/FModel/Views/Resources/Colors.xaml +++ b/FModel/Views/Resources/Colors.xaml @@ -48,6 +48,9 @@ + + + diff --git a/FModel/Views/Resources/Converters/FileToGeometryConverter.cs b/FModel/Views/Resources/Converters/FileToGeometryConverter.cs index cd620def..3b75a145 100644 --- a/FModel/Views/Resources/Converters/FileToGeometryConverter.cs +++ b/FModel/Views/Resources/Converters/FileToGeometryConverter.cs @@ -80,10 +80,14 @@ public class FileToGeometryConverter : IMultiValueConverter "bin" => ("DataTableIcon", "BinaryBrush"), "xml" => ("XmlIcon", "JsonXmlBrush"), "gitignore" => ("GitIcon", "GitBrush"), + "gitattributes" => ("GitIcon", "GitBrush"), "html" => ("HtmlIcon", "HtmlBrush"), "js" => ("JavaScriptIcon", "JavaScriptBrush"), "css" => ("CssIcon", "CssBrush"), "csv" => ("CsvIcon", "CsvBrush"), + "sql" => ("SQLIcon", "SQLBrush"), + "py" => ("PythonIcon", "PythonBrush"), + "cs" => ("CSharpIcon", "CSharpBrush"), _ => ("DataTableIcon", "NeutralBrush") }, diff --git a/FModel/Views/Resources/Icons.xaml b/FModel/Views/Resources/Icons.xaml index 59505988..56b90f8e 100644 --- a/FModel/Views/Resources/Icons.xaml +++ b/FModel/Views/Resources/Icons.xaml @@ -99,6 +99,9 @@ M14 2H6C4.9 2 4 2.9 4 4V20C4 21.1 4.9 22 6 22H18C19.1 22 20 21.1 20 20V8L14 2M15 16L13 20H10L12 16H9V11H15V16M13 9V3.5L18.5 9H13Z M12,2A2,2 0 0,1 14,4C14,4.74 13.6,5.39 13,5.73V7H14A7,7 0 0,1 21,14H22A1,1 0 0,1 23,15V18A1,1 0 0,1 22,19H21V20A2,2 0 0,1 19,22H5A2,2 0 0,1 3,20V19H2A1,1 0 0,1 1,18V15A1,1 0 0,1 2,14H3A7,7 0 0,1 10,7H11V5.73C10.4,5.39 10,4.74 10,4A2,2 0 0,1 12,2M7.5,13A2.5,2.5 0 0,0 5,15.5A2.5,2.5 0 0,0 7.5,18A2.5,2.5 0 0,0 10,15.5A2.5,2.5 0 0,0 7.5,13M16.5,13A2.5,2.5 0 0,0 14,15.5A2.5,2.5 0 0,0 16.5,18A2.5,2.5 0 0,0 19,15.5A2.5,2.5 0 0,0 16.5,13Z M14,12L10,8V11H2V13H10V16M20,18V6C20,4.89 19.1,4 18,4H6A2,2 0 0,0 4,6V9H6V6H18V18H6V15H4V18A2,2 0 0,0 6,20H18A2,2 0 0,0 20,18Z + M231.822 132.778c1.2-5.152 6.352-8.357 11.505-7.158 5.153 1.2 8.357 6.352 7.158 11.505l-20.773 88.972c-1.199 5.153-6.352 8.358-11.504 7.158-5.153-1.199-8.358-6.352-7.158-11.505l20.772-88.972zM21.123 259.247h16.714V52.395C37.837 23.601 61.438 0 90.232 0h230.505a7.998 7.998 0 016.39 3.183l96.876 104.809a7.937 7.937 0 012.118 5.411h.041v145.844h16.714c11.618 0 21.124 9.525 21.124 21.124v163.863c0 11.599-9.526 21.124-21.124 21.124h-17.058c-3.001 25.877-25.473 46.505-52.051 46.505H90.232c-26.72 0-49.082-20.526-52.055-46.505H21.123C9.526 465.358 0 455.853 0 444.234V280.371c0-11.619 9.506-21.124 21.123-21.124zm32.731 0h356.292V136.525h-32.219v-.033h-.124c-19.111-.302-34.068-5.373-44.736-14.486-11.073-9.458-17.282-22.969-18.52-39.763l-.075-1.098V16.016H90.232c-20.032 0-36.378 16.346-36.378 36.379v206.852zm355.795 206.111H54.344c2.862 17.157 17.981 30.488 35.888 30.488h283.535c17.803 0 32.993-13.394 35.882-30.488zM330.467 30.272V81.11c.913 12.412 5.265 22.192 12.97 28.775 7.822 6.682 19.34 10.418 34.49 10.676v-.03h32.219v-4.052l-79.679-86.207zM199.303 204.095c3.972 3.477 4.377 9.521.9 13.493-3.478 3.973-9.521 4.377-13.494.9l-36.354-31.854c-3.972-3.477-4.377-9.521-.9-13.493.295-.336.606-.644.933-.927l36.321-31.826c3.973-3.478 10.016-3.073 13.494.899 3.477 3.973 3.072 10.016-.9 13.494l-28.14 24.656 28.14 24.658zm77.99 14.393c-3.972 3.477-10.016 3.073-13.493-.9-3.478-3.972-3.073-10.016.899-13.493l28.14-24.658-28.14-24.656c-3.972-3.478-4.377-9.521-.899-13.494 3.477-3.972 9.521-4.377 13.493-.899l36.318 31.826c.328.283.639.591.933.927 3.477 3.972 3.073 10.016-.899 13.493l-36.352 31.854zM120.405 400.89l3.805-19.455c8.333 2.083 15.827 3.123 22.484 3.123 6.66 0 12.024-.27 16.101-.815v-9.755l-12.228-1.086c-11.051-.999-18.635-3.648-22.759-7.951-4.121-4.302-6.181-10.665-6.181-19.089 0-11.592 2.514-19.565 7.54-23.911 5.029-4.35 13.565-6.523 25.612-6.523 12.047 0 22.915 1.134 32.61 3.398l-3.398 18.978c-8.423-1.358-15.171-2.038-20.244-2.038s-9.375.226-12.908.68v9.416l9.783.952c11.865 1.177 20.063 4.008 24.591 8.492 4.531 4.484 6.795 10.71 6.795 18.682 0 5.706-.77 10.528-2.311 14.47-1.541 3.94-3.373 6.927-5.502 8.966-2.129 2.038-5.141 3.601-9.037 4.687-3.892 1.087-7.312 1.745-10.255 1.97-2.944.228-6.864.341-11.755.341-11.775 0-22.688-1.177-32.743-3.532zm128.036-1.631l-11.548 3.125c-15.219 0-25.414-3.926-30.707-11.684-2.674-3.923-4.621-8.266-5.708-13.111-1.087-4.844-1.628-10.712-1.628-17.595 0-15.491 2.899-26.789 8.695-33.9 5.797-7.109 16.212-10.666 31.249-10.666 15.036 0 25.498 3.579 31.386 10.734 5.887 7.156 8.83 18.432 8.83 33.832 0 11.502-2.399 20.786-7.199 27.851l9.782 5.708-7.067 15.623-26.085-4.754v-5.163zm-20.382-18.072h11.279c3.714 0 6.407-.429 8.083-1.29 1.675-.861 2.514-2.83 2.514-5.909v-35.325h-11.414c-3.62 0-6.272.429-7.947 1.29-1.675.861-2.515 2.834-2.515 5.909v35.325zm115.536 21.197h-54.349v-84.917h27.175v63.179h27.174v21.738z + M19.14,7.5A2.86,2.86 0 0,1 22,10.36V14.14A2.86,2.86 0 0,1 19.14,17H12C12,17.39 12.32,17.96 12.71,17.96H17V19.64A2.86,2.86 0 0,1 14.14,22.5H9.86A2.86,2.86 0 0,1 7,19.64V15.89C7,14.31 8.28,13.04 9.86,13.04H15.11C16.69,13.04 17.96,11.76 17.96,10.18V7.5H19.14M14.86,19.29C14.46,19.29 14.14,19.59 14.14,20.18C14.14,20.77 14.46,20.89 14.86,20.89A0.71,0.71 0 0,0 15.57,20.18C15.57,19.59 15.25,19.29 14.86,19.29M4.86,17.5C3.28,17.5 2,16.22 2,14.64V10.86C2,9.28 3.28,8 4.86,8H12C12,7.61 11.68,7.04 11.29,7.04H7V5.36C7,3.78 8.28,2.5 9.86,2.5H14.14C15.72,2.5 17,3.78 17,5.36V9.11C17,10.69 15.72,11.96 14.14,11.96H8.89C7.31,11.96 6.04,13.24 6.04,14.82V17.5H4.86M9.14,5.71C9.54,5.71 9.86,5.41 9.86,4.82C9.86,4.23 9.54,4.11 9.14,4.11C8.75,4.11 8.43,4.23 8.43,4.82C8.43,5.41 8.75,5.71 9.14,5.71Z + M7878 15350 c-162 -15 -335 -63 -471 -131 -146 -74 -5492 -3082 -5567 -3133 -41 -28 -120 -96 -175 -151 -189 -189 -307 -411 -362 -682 -17 -84 -18 -245 -18 -3253 0 -3008 1 -3169 18 -3253 50 -249 159 -466 323 -645 64 -71 188 -173 269 -224 102 -64 5469 -3075 5535 -3106 367 -170 814 -166 1165 10 75 38 5370 3012 5482 3079 326 196 543 505 620 886 17 84 18 245 18 3253 0 3008 -1 3169 -18 3253 -55 271 -173 493 -362 682 -55 55 -134 123 -175 151 -75 51 -5421 3059 -5568 3133 -203 102 -479 152 -714 131z m591 -3534 c526 -70 1018 -235 1456 -489 298 -172 642 -446 866 -689 117 -126 259 -298 259 -312 -1 -6 -301 -184 -667 -396 -366 -212 -680 -393 -697 -403 l-31 -18 -85 82 c-583 568 -1404 778 -2184 559 -501 -141 -952 -471 -1245 -910 -249 -373 -374 -786 -374 -1240 0 -372 82 -709 250 -1030 337 -644 947 -1075 1678 -1185 133 -21 439 -23 570 -6 499 66 958 288 1304 631 l84 82 32 -18 c892 -514 1364 -791 1365 -800 1 -32 -300 -365 -462 -511 -583 -528 -1275 -856 -2043 -967 -200 -29 -382 -39 -638 -33 -277 6 -411 20 -672 72 -1512 301 -2719 1521 -3009 3040 -52 276 -61 378 -61 725 0 355 9 453 66 747 255 1307 1201 2410 2461 2867 312 113 669 190 1018 220 118 10 641 -2 759 -18z m3371 -2536 l0 -320 320 0 320 0 0 320 0 320 320 0 320 0 0 -320 0 -320 320 0 320 0 0 -320 0 -320 -320 0 -320 0 0 -320 0 -320 320 0 320 0 0 -320 0 -320 -320 0 -320 0 0 -320 0 -320 -320 0 -320 0 0 320 0 320 -320 0 -320 0 0 -320 0 -320 -320 0 -320 0 0 320 0 320 -320 0 -320 0 0 320 0 320 320 0 320 0 0 320 0 320 -320 0 -320 0 0 320 0 320 320 0 320 0 0 320 0 320 320 0 320 0 0 -320z M11840 8000 l0 -320 320 0 320 0 0 320 0 320 -320 0 -320 0 0 -320z M13,9V3.5L18.5,9M6,2C4.89,2 4,2.89 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2H6 ZM12,11A3,3 0 1,0 12,17A3,3 0 0,0 12,11 ZM12,12.5L14,16H13L12,14.5L11,16H10L12,12.5Z