diff --git a/src/Watson.CLI/Program.cs b/src/Watson.CLI/Program.cs index 83bda62..fa80439 100644 --- a/src/Watson.CLI/Program.cs +++ b/src/Watson.CLI/Program.cs @@ -1,5 +1,7 @@ -using Spectre.Console; +using AssetsTools.NET.Extra; +using Spectre.Console; using Watson.Lib.Game.AI_TheSomniumFiles2.Enums; +using Watson.Lib.IO; using Watson.Program.Utils; AnsiConsole.Markup("[purple]Welcome to Watson![/] - [yellow]v1.1.0[/]\n"); @@ -9,11 +11,11 @@ { case HandlerArgs.Mode.SVS: AnsiConsole.Progress() - .Start(ctx => + .Start(ctx => { var svs = new Watson.Lib.Game.neptunia_sisters_vs_sisters.Game(arg.GamePath, ctx); svs.Proccess(); - + // Extract to Po if (arg.extract) { @@ -25,15 +27,15 @@ AnsiConsole.MarkupLine("[green]Juego - AI: The Somnium Files - Nirvana Initiative[/]"); AnsiConsole.Status() .AutoRefresh(true) - .Start("Iniciando...", ctx => + .Start("Iniciando...", ctx => { // Simulate some work ctx.Status("Leyendo carpeta del juego..."); ctx.Spinner(Spinner.Known.Circle); ctx.SpinnerStyle(Style.Parse("yellow")); var psync2 = new Watson.Lib.Game.AI_TheSomniumFiles2.Game(arg.GamePath, LanguageType.en, ctx); - - + + // Update the status and spinner ctx.Status("Procesando Archivos..."); psync2.Load(); @@ -47,8 +49,39 @@ } }); break; + case HandlerArgs.Mode.Unity3D: + AnsiConsole.MarkupLine("[green]Modo - Unity3D[/]"); + AnsiConsole.Status() + .AutoRefresh(true) + .Start("Iniciando...", ctx => + { + ctx.Status("Leyendo archivo..."); + ctx.Spinner(Spinner.Known.Circle); + ctx.SpinnerStyle(Style.Parse("yellow")); + UnityAssetFile.LoadAndExtractUnity3D(arg.filePath); + }); + break; + case HandlerArgs.Mode.CocoDrilo: + AnsiConsole.MarkupLine("[green]Juego - Later Alligator[/]"); + AnsiConsole.Status() + .AutoRefresh(true) + .Start("Iniciando...", ctx => + { + ctx.Spinner(Spinner.Known.Circle); + ctx.SpinnerStyle(Style.Parse("yellow")); + var cocodrilo = new Watson.Lib.Game.LaterAlligator.Game(arg.GamePath, ctx); + + if (arg.extract) + { + cocodrilo.Proccess(); + cocodrilo.Export(arg.OutPut); + } + + cocodrilo.Import(arg.PoPath); + }); + break; case HandlerArgs.Mode.Help: default: HandlerArgs.PrintInfo(); break; -} \ No newline at end of file +} diff --git a/src/Watson.CLI/Utils/HandlerArgs.cs b/src/Watson.CLI/Utils/HandlerArgs.cs index 0b2f1b0..0562eca 100644 --- a/src/Watson.CLI/Utils/HandlerArgs.cs +++ b/src/Watson.CLI/Utils/HandlerArgs.cs @@ -11,7 +11,9 @@ public enum Mode HoloError, Windose, SVS, - Psync2 + Psync2, + CocoDrilo, + Unity3D } public HandlerArgs(string[] raw_args) @@ -24,11 +26,15 @@ public HandlerArgs(string[] raw_args) new( new[] { "--windose" }, () => OperationMode = Mode.Windose), new( new[] { "--NeptuniaSVS", "-svs" }, () => OperationMode = Mode.SVS), new( new[] { "--AI2", "-ai2" }, () => OperationMode = Mode.Psync2), + new( new[] { "--coco" }, () => OperationMode = Mode.CocoDrilo), + new( new[] { "--unity" }, () => OperationMode = Mode.Unity3D), // Args new(new[] { "--gamepath" }, x => GamePath = x), new(new[] { "--output", "-o" }, x => OutPut = x), - new(new[] { "--extract", "-x" }, () => extract = true) + new(new[] { "--extract", "-x" }, () => extract = true), + new(new[] { "--import", "-i" }, x => PoPath = x), + new(new[] { "--file", "-f" }, x => filePath = x) }; for (var i = 0; i < raw_args.Length; ++i) @@ -40,30 +46,22 @@ public HandlerArgs(string[] raw_args) else handler.Invoke(handler.RequiresArg ? raw_args[++i] : null!); } + + if (import && string.IsNullOrEmpty(PoPath)) + throw new Exception("Import requires a Po file path"); } public Mode? OperationMode { get; private set; } public string? GamePath { get; private set; } + public string? filePath { get; private set; } public string? OutPut { get; private set; } = "out"; + public string? PoPath { get; private set; } public bool extract { get; private set; } + public bool import { get; private set; } public static void PrintInfo() { AnsiConsole.Markup("[purple]Watson.CLI.exe[/] [red](MODE)[/] [yellow](OPTIONS)[/]"); - /*cmdutils.print("Operation Modes:"); - cmdutils.print(" --Hololy Download and decrypt Hololy models."); - cmdutils.print(" --HoloEarth Download HoloEarths."); - cmdutils.print(" --HololiveError Decrypt HololiveError bundles."); - cmdutils.print("General options: "); - cmdutils.print(" --output, -o Set the output folder."); - cmdutils.print("Hololive Error options:"); - cmdutils.print(" --input, -i Set the input file to decrypt."); - cmdutils.print(" --hash Set the hash of the bundle"); - cmdutils.print("Hololy options:"); - cmdutils.print(" --dev Set the dev server"); - cmdutils.print(" --list, -l List the models"); - cmdutils.print(" --download, -d Download the models"); - cmdutils.print(" --model Download the model by name");*/ } private class Handler @@ -95,4 +93,4 @@ public void Invoke(string arg) Fn!(); } } -} \ No newline at end of file +} diff --git a/src/Watson.CLI/Watson.CLI.csproj b/src/Watson.CLI/Watson.CLI.csproj index b0e6a3e..7692300 100644 --- a/src/Watson.CLI/Watson.CLI.csproj +++ b/src/Watson.CLI/Watson.CLI.csproj @@ -7,6 +7,7 @@ enable latest false + true @@ -14,7 +15,7 @@ - + diff --git a/src/Watson.Lib/Game/AI_TheSomniumFiles2/Game.cs b/src/Watson.Lib/Game/AI_TheSomniumFiles2/Game.cs index 4f750b6..34aff5e 100644 --- a/src/Watson.Lib/Game/AI_TheSomniumFiles2/Game.cs +++ b/src/Watson.Lib/Game/AI_TheSomniumFiles2/Game.cs @@ -70,7 +70,7 @@ public void Proccess() foreach (var sprite in m_Sprites) sprite.Load(); } - public void Import() + public void Import(string poPath) { throw new NotImplementedException(); } @@ -148,4 +148,4 @@ public string[] listFonts(string filter = "") return fonts.ToArray(); } -} \ No newline at end of file +} diff --git a/src/Watson.Lib/Game/IGame.cs b/src/Watson.Lib/Game/IGame.cs index d478211..6ac65ff 100644 --- a/src/Watson.Lib/Game/IGame.cs +++ b/src/Watson.Lib/Game/IGame.cs @@ -6,7 +6,7 @@ public interface IGame public void Proccess(); - public void Import(); + public void Import(string poPath = ""); public void Export(string outpath = "out"); -} \ No newline at end of file +} diff --git a/src/Watson.Lib/Game/LaterAlligator/Game.cs b/src/Watson.Lib/Game/LaterAlligator/Game.cs new file mode 100644 index 0000000..26be6a6 --- /dev/null +++ b/src/Watson.Lib/Game/LaterAlligator/Game.cs @@ -0,0 +1,173 @@ +namespace Watson.Lib.Game.LaterAlligator; + +using AssetsTools.NET; +using AssetsTools.NET.Extra; +using IO; +using Spectre.Console; +using Utils; +using Yarhl.FileSystem; +using Yarhl.IO; +using Yarhl.Media.Text; + +public class Game : IGame +{ + public string gamepath { get; set; } + private string gamedatapath { get; set; } + private string assemblyFolder { get; set; } + private string ExtractedAssetsFolder { get; set; } + private StatusContext ctx { get; set; } + + private Dictionary Say = new Dictionary(); + private Dictionary InvokeNextLineAsync = new Dictionary(); + + public Game(string gamepath, StatusContext ctx) + { + this.gamepath = gamepath; + this.ctx = ctx; + Load(); + } + + public void Load() + { + gamedatapath = Path.Combine(gamepath, "LaterAlligator_Data"); + assemblyFolder = Path.Combine(gamedatapath, "Managed"); + ExtractedAssetsFolder = TempDirectory.CreateTempDirectory(); + ctx.Status("Extrayendo Archivos..."); + } + + public void Proccess() + { + ctx.Status("Leyendo Archivos..."); + foreach (string filePath in Directory.GetFiles(gamedatapath, "*.bundle", SearchOption.AllDirectories)) { + /*if (!filePath.Contains("scenes_scenes_ending-credits.bundle")) + continue;*/ + + AnsiConsole.MarkupLine("[yellow]Leyendo Archivo:[/] " + Path.GetFileName(filePath)); + var m_assetfile = new UnityAssetFile(filePath, gamedatapath); + List storyTexts = new List(); + List nextLineAsync = new List(); + foreach (AssetFileInfo m_monobehaviour in m_assetfile.GetAssetsOfType(AssetClassID.MonoBehaviour)) { + var deserialized = + m_assetfile.AM.GetBaseField(m_assetfile.Assets, m_monobehaviour); + try + { + if (!deserialized["targetMethod"].IsDummy && + deserialized["targetMethod"].Value.AsString == "NextLineAsync") { + foreach (AssetTypeValueField objValue in deserialized["methodParameters.Array"]) { + if (objValue["objValue"].IsDummy || objValue["objValue"]["stringValue"].IsDummy) + continue; + nextLineAsync.Add(objValue["objValue"]["stringValue"].Value.AsString); + } + } + + if (!deserialized["storyText"].IsDummy) + storyTexts.Add(deserialized.Get("storyText").Value.AsString); + } catch (Exception e) { +#if DEBUG + AnsiConsole.WriteException(e); +#endif + } + } + + if (storyTexts.Count > 0) + Say.Add(Path.GetFileName(filePath), storyTexts.ToArray()); + if (nextLineAsync.Count > 0) + InvokeNextLineAsync.Add(Path.GetFileName(filePath), nextLineAsync.ToArray()); + + m_assetfile.Close(); + } + } + + public void Import(string poPath) + { + ctx.Status("Importando Archivos..."); + foreach (string filePath in Directory.GetFiles(gamedatapath, "*.bundle", SearchOption.AllDirectories)) { + + if (!File.Exists(Path.Combine(poPath, $"{Path.GetFileName(filePath)}.InvokeNextLineAsync.po")) && !File.Exists(Path.Combine(poPath, $"{Path.GetFileName(filePath)}.Say.po"))) + continue; + + AnsiConsole.MarkupLine("[yellow]Leyendo Archivo:[/] " + Path.GetFileName(filePath)); + UnityAssetFile m_assetfile = new UnityAssetFile(filePath, gamedatapath); + + if (File.Exists(Path.Combine(poPath, $"{Path.GetFileName(filePath)}.InvokeNextLineAsync.po"))) { + using var Po = NodeFactory.FromFile(Path.Combine(poPath, $"{Path.GetFileName(filePath)}.InvokeNextLineAsync.po"), FileOpenMode.Read); + Po.TransformWith(new Binary2Po()); + var po = Po.GetFormatAs(); + int index = 0; + foreach (AssetFileInfo m_monobehaviour in m_assetfile.GetAssetsOfType(AssetClassID.MonoBehaviour)) { + + var deserialized = m_assetfile.AM.GetBaseField(m_assetfile.Assets, m_monobehaviour); + + try { + if (!deserialized["targetMethod"].IsDummy && + deserialized["targetMethod"].Value.AsString == "NextLineAsync") { + foreach (AssetTypeValueField objValue in deserialized["methodParameters.Array"]) { + if (objValue["objValue"].IsDummy || objValue["objValue"]["stringValue"].IsDummy) + continue; + + objValue["objValue"]["stringValue"].Value.AsString = + !string.IsNullOrEmpty(po.Entries[index].Translated) + ? po.Entries[index].Translated.Replace("\n", "\r\n") + : po.Entries[index].Original.Replace("\n", "\r\n"); + + m_monobehaviour.SetNewData(objValue); + index++; + } + } + } catch (Exception e) { + // ignored + } + } + } + + if (File.Exists(Path.Combine(poPath, $"{Path.GetFileName(filePath)}.Say.po"))) { + using var Po = NodeFactory.FromFile(Path.Combine(poPath, $"{Path.GetFileName(filePath)}.Say.po"), FileOpenMode.Read); + Po.TransformWith(new Binary2Po()); + var po = Po.GetFormatAs(); + int index = 0; + foreach (AssetFileInfo m_monobehaviour in m_assetfile.GetAssetsOfType(AssetClassID.MonoBehaviour)) { + try { + + var deserialized = m_assetfile.AM.GetBaseField(m_assetfile.Assets, m_monobehaviour); + + if (!deserialized["storyText"].IsDummy) { + deserialized.Get("storyText").Value.AsString = + !string.IsNullOrEmpty(po.Entries[index].Translated) + ? po.Entries[index].Translated.Replace("\n", "\r\n") + : po.Entries[index].Original.Replace("\n", "\r\n"); + m_monobehaviour.SetNewData(deserialized); + index++; + } + + + + } catch (Exception e) { + + } + } + } + + // Save the file + Utils.Helpers.AssetHelper.Save(m_assetfile); + m_assetfile.Close(); + } + } + + public void Export(string outputPath = "out") + { + ctx.Status("Exportando Archivos..."); + Directory.CreateDirectory(outputPath); + foreach (var (fileName, texts) in Say) + { + AnsiConsole.MarkupLine($"[yellow]Exportando Archivo:[/] {fileName}"); + var po = new ArrayString2Po(fileName, "Say").Convert(texts); + new Po2Binary().Convert(po).Stream?.WriteTo(Path.Combine(outputPath, $"{fileName}.Say.po")); + } + + foreach (var (fileName, texts) in InvokeNextLineAsync) { + AnsiConsole.MarkupLine($"[yellow]Exportando Archivo:[/] {fileName}"); + var po = new ArrayString2Po(fileName, "InvokeNextLineAsync").Convert(texts); + new Po2Binary().Convert(po).Stream?.WriteTo(Path.Combine(outputPath, $"{fileName}.InvokeNextLineAsync.po")); + } + } +} diff --git a/src/Watson.Lib/Game/Windose/Game.cs b/src/Watson.Lib/Game/Windose/Game.cs index 26617fa..9c411ce 100644 --- a/src/Watson.Lib/Game/Windose/Game.cs +++ b/src/Watson.Lib/Game/Windose/Game.cs @@ -461,7 +461,7 @@ public void Proccess() } } - public void Import() + public void Import(string poPath = "") { throw new NotImplementedException(); } @@ -1127,4 +1127,4 @@ public Po Convert(yakujoMaster.Param[] source) } #endregion -} \ No newline at end of file +} diff --git a/src/Watson.Lib/Game/neptunia-sisters-vs-sisters/Game.cs b/src/Watson.Lib/Game/neptunia-sisters-vs-sisters/Game.cs index 5f6bff4..c1da94f 100644 --- a/src/Watson.Lib/Game/neptunia-sisters-vs-sisters/Game.cs +++ b/src/Watson.Lib/Game/neptunia-sisters-vs-sisters/Game.cs @@ -39,7 +39,7 @@ public void Load() AnsiConsole.Markup("[yellow]Neptunia: Sisters VS Sisters mode![/]\n"); var task = ctx?.AddTask("[green]Searching assets[/]"); gamedatapath = Path.Combine(gamepath, $"{gamename}_Data"); - + foreach (var files in Directory.GetFiles(gamedatapath, "*.*", SearchOption.AllDirectories) .Where(file => Regex.IsMatch(file, CSV_REGEX))) csvassets.Add(files); @@ -48,13 +48,13 @@ public void Load() public void Proccess() { - + var task = ctx?.AddTask("[green]Processing assets[/]"); float proInc = 100.0f / (csvassets.Count -1); foreach (var file in csvassets) { var am = new UnityAssetFile(file, gamedatapath); - + // CSV var text = new TextAsset(am); text.Load(); @@ -125,15 +125,15 @@ public void Proccess() csventry.unk_4 = entrys[14]; csvs.Add(csventry); } - + var arr = csvs.ToArray(); if (arr.Length <= 0) continue; csvfiles.Add(csv.Value.Item2["m_Name"].AsString, arr); } - + // String DB - + var dbobject = new DbStringObject(am); dbobject.Load(); @@ -156,23 +156,23 @@ public void Proccess() s.krText_ = test["krText_"].AsString; s.tag_ = test["tag_"].AsString; s.extend_ = new DbExtendString(test["extend_"]["Comment_"].AsString); - + list.Add(s); } } } - + var arr = list.ToArray(); if (arr.Length <= 0) continue; dbstrings.Add(dbobjets.Value.Item2["m_Name"].AsString, arr); } - + task?.Increment(proInc); } } - public void Import() + public void Import(string poPath = "") { throw new NotImplementedException(); } @@ -180,9 +180,9 @@ public void Import() public void Export(string outpath = "out") { var task = ctx?.AddTask("[green]Converting to po[/]"); - + float proInc = 100.0f / (csvfiles.Count + dbstrings.Count); - + if (!Directory.Exists(outpath)) Directory.CreateDirectory(outpath); @@ -265,4 +265,4 @@ public Po Convert((string, DbStringMake[]) source) return po; } -} \ No newline at end of file +} diff --git a/src/Watson.Lib/IO/ArrayString2Po.cs b/src/Watson.Lib/IO/ArrayString2Po.cs new file mode 100644 index 0000000..dae078c --- /dev/null +++ b/src/Watson.Lib/IO/ArrayString2Po.cs @@ -0,0 +1,42 @@ +namespace Watson.Lib.IO; + +using Yarhl.FileFormat; +using Yarhl.Media.Text; + +public class ArrayString2Po : IConverter +{ + private string preContext { get; set; } + private string Name { get; set; } + + public ArrayString2Po(string Name, string preContext = "") + { + this.Name = Name; + this.preContext = preContext; + + if (preContext == string.Empty) + preContext = "NoPreContext"; + } + + public Po Convert(string[] source) + { + var currentCulture = Thread.CurrentThread.CurrentCulture; + var po = new Po + { + Header = new PoHeader("Watson", "d3fau4@not-d3fau4.com", currentCulture.Name) + { + LanguageTeam = "Any" + } + }; + + for (int i = 0; i < source.Length; i++) { + var txt = !source[i].Equals(string.Empty) ? source[i] : "{EMPTY}"; + po.Add(new PoEntry + { + Original = txt.Replace("\r\n", "\n"), + Context = $"{Name}.{preContext}.{i}" + }); + } + + return po; + } +} diff --git a/src/Watson.Lib/IO/UnityAssetFile.cs b/src/Watson.Lib/IO/UnityAssetFile.cs index f8011c9..762e238 100644 --- a/src/Watson.Lib/IO/UnityAssetFile.cs +++ b/src/Watson.Lib/IO/UnityAssetFile.cs @@ -4,6 +4,8 @@ namespace Watson.Lib.IO; +using Spectre.Console; + public class UnityAssetFile { public AssetsManager AM; @@ -30,7 +32,7 @@ public UnityAssetFile(string file) } catch (Exception ex) { - // Si recibe un error de que el archivo es muy pequeño intentar abrir como AssetBundle. + // Si recibe un error de que el archivo es muy pequeño intentar abrir como AssetBundle. if (ex.Message.Contains("too small") || ex.Message.Contains("Unable to read beyond the end")) { // Descargar lo que haya conseguido cargar. @@ -61,7 +63,7 @@ public UnityAssetFile(string file, string DataFolder) } catch (Exception ex) { - // Si recibe un error de que el archivo es muy pequeño intentar abrir como AssetBundle. + // Si recibe un error de que el archivo es muy pequeño intentar abrir como AssetBundle. if (ex.Message.Contains("too small") || ex.Message.Contains("Unable to read beyond the end")) { // Descargar lo que haya conseguido cargar. @@ -70,10 +72,15 @@ public UnityAssetFile(string file, string DataFolder) Bundle = AM.LoadBundleFile(file); // Siempre index 0 ya que es el que contiene todos los archivos - Assets = AM.LoadAssetsFileFromBundle(Bundle, 0, true); + foreach (string fileName in Bundle.file.GetAllFileNames()) { + if (!fileName.Contains(".sharedAssets") && Bundle.file.BlockAndDirInfo.DirectoryInfos[Bundle.file.GetFileIndex(fileName)].Flags == 4) { + Assets = AM.LoadAssetsFileFromBundle(Bundle, Bundle.file.GetFileIndex(fileName), true); + break; + } + } AM.LoadClassPackage(new MemoryStream(Resources.Resources.classdata)); - AM.LoadClassDatabaseFromPackage(Assets.file.Metadata.UnityVersion); + AM.LoadClassDatabaseFromPackage(Bundle.file.Header.EngineVersion); IsBundle = true; } } @@ -84,6 +91,40 @@ public UnityAssetFile(string file, string DataFolder) } } + public static void LoadAndExtractUnity3D(string path, string OutPut = "out") + { + var AM = new AssetsManager(); + var a = AM.LoadBundleFile(path, true); + try + { + Directory.CreateDirectory(OutPut); + for (int i = 0; i < 10000; i++) + { + AM.LoadAssetsFileFromBundle(a, i, false); + foreach (AssetsFileInstance assetsFileInstance in a.loadedAssetsFiles) + { + var filePath = Path.Combine(OutPut, assetsFileInstance.name); + if (File.Exists(filePath)) continue; + AnsiConsole.MarkupLine($"[yellow]Extrayendo archivo: [/] {assetsFileInstance.name}"); + using (var fileStream = File.Create(filePath)) + { + if (assetsFileInstance.AssetsStream.CanSeek) + { + assetsFileInstance.AssetsStream.Position = 0; + } + assetsFileInstance.AssetsStream.CopyTo(fileStream); + } + } + } + } + catch (Exception e) + { +#if DEBUG + AnsiConsole.MarkupLine($"[red]Error: [/] {e.Message}"); +#endif + } + } + public void Close() { AM.UnloadAll(); @@ -95,4 +136,4 @@ public List GetAssetsOfType(AssetClassID ID) foreach (var inf in Assets.file.GetAssetsOfType((int)ID)) list.Add(inf); return list; } -} \ No newline at end of file +} diff --git a/src/Watson.Lib/Resources/classdata.tpk b/src/Watson.Lib/Resources/classdata.tpk index 34add0f..ce5359d 100644 Binary files a/src/Watson.Lib/Resources/classdata.tpk and b/src/Watson.Lib/Resources/classdata.tpk differ diff --git a/src/Watson.Lib/Utils/Helpers.cs b/src/Watson.Lib/Utils/Helpers.cs index 3bbd623..fa3ecd6 100644 --- a/src/Watson.Lib/Utils/Helpers.cs +++ b/src/Watson.Lib/Utils/Helpers.cs @@ -19,13 +19,18 @@ public static void Save(UnityAssetFile assetFile, if (assetFile.IsBundle) { - assetFile.Bundle.file.BlockAndDirInfo.DirectoryInfos[0].SetNewData(assetFile.Assets.file); + assetFile.Bundle.file.BlockAndDirInfo.DirectoryInfos[assetFile.Bundle.file.GetFileIndex(assetFile.Assets.name)].SetNewData(assetFile.Assets.file); using (AssetsFileWriter writer = new AssetsFileWriter("TMP.unity3d")) { assetFile.Bundle.file.Write(writer); } assetFile.AM.UnloadAll(true); - File.Move("TMP.unity3d", assetFile.AssetName, true); + + //Create directory output + if (!Directory.Exists("out_assets")) + Directory.CreateDirectory("out_assets"); + + File.Move("TMP.unity3d", Path.Combine("out_assets", assetFile.AssetName), true); } else { @@ -84,4 +89,4 @@ public static class TextureHelper return texFile.pictureData; } -} \ No newline at end of file +} diff --git a/src/Watson.Lib/Utils/TempDirectory.cs b/src/Watson.Lib/Utils/TempDirectory.cs new file mode 100644 index 0000000..725e7b3 --- /dev/null +++ b/src/Watson.Lib/Utils/TempDirectory.cs @@ -0,0 +1,24 @@ +namespace Watson.Lib.Utils; + +using System; +using System.IO; + +public class TempDirectory +{ + public static string CreateTempDirectory() + { + // Obtener la ruta del directorio temporal del sistema + string tempPath = Path.GetTempPath(); + + // Generar un nombre único para la nueva carpeta temporal + string tempDirectoryName = Guid.NewGuid().ToString(); + + // Combinar la ruta del directorio temporal con el nuevo nombre de carpeta + string tempDirectoryFullPath = Path.Combine(tempPath, tempDirectoryName); + + // Crear la carpeta + Directory.CreateDirectory(tempDirectoryFullPath); + + return tempDirectoryFullPath; + } +} diff --git a/src/Watson.Lib/Watson.Lib.csproj b/src/Watson.Lib/Watson.Lib.csproj index b1f836f..9f4a458 100644 --- a/src/Watson.Lib/Watson.Lib.csproj +++ b/src/Watson.Lib/Watson.Lib.csproj @@ -6,6 +6,7 @@ latest true net8.0 + true @@ -13,12 +14,12 @@ - - + + - - + + diff --git a/src/Watson.Test/Watson.Test.csproj b/src/Watson.Test/Watson.Test.csproj index 89acb0c..e8761c9 100644 --- a/src/Watson.Test/Watson.Test.csproj +++ b/src/Watson.Test/Watson.Test.csproj @@ -10,10 +10,13 @@ - - - - + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive +