diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fbf9197 --- /dev/null +++ b/.gitignore @@ -0,0 +1,340 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- Backup*.rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb \ No newline at end of file diff --git a/SaveExif.sln b/SaveExif.sln new file mode 100644 index 0000000..47acb1c --- /dev/null +++ b/SaveExif.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.1.32414.318 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SaveExif", "SaveExif\SaveExif.csproj", "{39BD8749-F45A-4623-97F3-0572E74ED463}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {39BD8749-F45A-4623-97F3-0572E74ED463}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {39BD8749-F45A-4623-97F3-0572E74ED463}.Debug|Any CPU.Build.0 = Debug|Any CPU + {39BD8749-F45A-4623-97F3-0572E74ED463}.Release|Any CPU.ActiveCfg = Release|Any CPU + {39BD8749-F45A-4623-97F3-0572E74ED463}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {10594211-D0E4-4804-B970-7D1A0A841034} + EndGlobalSection +EndGlobal diff --git a/SaveExif/Properties/AssemblyInfo.cs b/SaveExif/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..38ea947 --- /dev/null +++ b/SaveExif/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// アセンブリに関する一般情報は以下の属性セットをとおして制御されます。 +// 制御されます。アセンブリに関連付けられている情報を変更するには、 +// これらの属性値を変更してください。 +[assembly: AssemblyTitle("SaveExif")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("SaveExif")] +[assembly: AssemblyCopyright("Copyright © 2022")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// ComVisible を false に設定すると、このアセンブリ内の型は COM コンポーネントから +// 参照できなくなります。COM からこのアセンブリ内の型にアクセスする必要がある場合は、 +// その型の ComVisible 属性を true に設定してください。 +[assembly: ComVisible(false)] + +// このプロジェクトが COM に公開される場合、次の GUID が typelib の ID になります +[assembly: Guid("39bd8749-f45a-4623-97f3-0572e74ed463")] + +// アセンブリのバージョン情報は、以下の 4 つの値で構成されています: +// +// メジャー バージョン +// マイナー バージョン +// ビルド番号 +// リビジョン +// +// すべての値を指定するか、次を使用してビルド番号とリビジョン番号を既定に設定できます +// 既定値にすることができます: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/SaveExif/SaveExif.cs b/SaveExif/SaveExif.cs new file mode 100644 index 0000000..1c3d535 --- /dev/null +++ b/SaveExif/SaveExif.cs @@ -0,0 +1,225 @@ +extern alias lib1; +using FrooxEngine; +using HarmonyLib; +using NeosModLoader; +using System; +using System.Threading.Tasks; +using System.IO; +using MimeDetective; +using CodeX; +using BaseX; +using System.Drawing; +using System.Drawing.Imaging; +using System.Collections.Generic; + +namespace SaveExif +{ + public class SaveExif : NeosMod + { + public override string Name => "SaveExif"; + public override string Author => "kka429"; + public override string Version => "1.0.0"; + public override string Link => "https://github.com/rassi0429/SaveExif"; // this line is optional and can be omitted + + // Exif Type: https://github.com/mono/libgdiplus/blob/main/src/gdiplusimaging.h + + public override void OnEngineInit() + { + Harmony harmony = new Harmony("dev.kokoa.saveexif"); + harmony.PatchAll(); + } + + [HarmonyPatch(typeof(lib1::WindowsPlatformConnector), "NotifyOfScreenshot", new Type[] { typeof(World), typeof(string), typeof(ScreenshotType), typeof(DateTime) })] + class Patch + { + + private static void SetProperty(ref System.Drawing.Imaging.PropertyItem prop, int iId, string sTxt) + { + int iLen = sTxt.Length + 1; + byte[] bTxt = new Byte[iLen]; + for (int i = 0; i < iLen - 1; i++) + bTxt[i] = (byte)sTxt[i]; + bTxt[iLen - 1] = 0x00; + prop.Id = iId; + prop.Type = 2; + prop.Value = bTxt; + prop.Len = iLen; + } + + static bool Prefix(bool ___keepOriginalScreenshotFormat, lib1::WindowsPlatformConnector __instance, World world, string file, ScreenshotType type, DateTime timestamp) + { + __instance.Engine.GlobalCoroutineManager.StartTask((Func)(async () => + { + await new ToBackground(); + string pictures = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures); + pictures = Path.Combine(pictures, "Neos VR"); + Directory.CreateDirectory(pictures); + string filename = timestamp.ToLocalTime().ToString("yyyy-MM-dd HH.mm.ss"); //FIX LOCALTIME + string extension = ___keepOriginalScreenshotFormat ? Path.GetExtension(file) : ".jpg"; + if (string.IsNullOrWhiteSpace(extension)) + { + FileType fileType = new FileInfo(file).GetFileType(); + if (fileType != null) + extension = "." + fileType.Extension; + } + await lib1::WindowsPlatformConnector.ScreenshotSemaphore.WaitAsync().ConfigureAwait(false); + try + { + int num = 1; + string str1; + do + { + string str2 = filename; + if (num > 1) + str2 += string.Format(" ({0})", (object)num); + str1 = Path.Combine(pictures, str2 + extension); + ++num; + } + while (File.Exists(str1)); + if (___keepOriginalScreenshotFormat) + { + File.Copy(file, str1); + File.SetAttributes(str1, FileAttributes.Normal); + } + else + { + TextureEncoder.ConvertToJPG(file, file + ".tmp"); + Image img = Image.FromFile(file + ".tmp"); + + PropertyItem w = img.PropertyItems[0]; + SetProperty(ref w, 272, "NeosCamera"); + img.SetPropertyItem(w); + PropertyItem w2 = img.PropertyItems[0]; + SetProperty(ref w2, 271, "FrooxEngine"); + img.SetPropertyItem(w2); + PropertyItem w4 = img.PropertyItems[0]; + w4.Id = 0x9003; //撮影日時 + w4.Type = 2; + w4.Len = 20; + w4.Value = System.Text.Encoding.ASCII.GetBytes(timestamp.ToLocalTime().ToString("yyyy:MM:dd HH:mm:ss")); + img.SetPropertyItem(w4); + + PropertyItem w5 = img.PropertyItems[0]; + w5.Id = 0x9286; //COMMENT + w5.Type = 7; + + var locationName = Engine.Current.WorldManager.FocusedWorld.GetSessionInfo().Name; + var locationUrl = Engine.Current.WorldManager.FocusedWorld.IsPublic ? Engine.Current.WorldManager.FocusedWorld.CorrespondingRecord?.URL.ToString() : "private"; + var hostUserId = Engine.Current.WorldManager.FocusedWorld.HostUser.UserID; + var hostUserName = Engine.Current.WorldManager.FocusedWorld.HostUser.UserName; + var timeTaken = timestamp.ToString(); + var takeUserId = Engine.Current.WorldManager.FocusedWorld.LocalUser.UserID; + var takeUserName = Engine.Current.WorldManager.FocusedWorld.LocalUser.UserName; + var neosVersion = Engine.Version.ToString(); + var _presentUser = Engine.Current.WorldManager.FocusedWorld.AllUsers; + List presentUserIdArray = new List(); + List presentUserNameArray = new List(); + foreach(var user in _presentUser) + { + presentUserIdArray.Add(user.UserID); + presentUserNameArray.Add(user.UserName); + } + + string str = $"{{\"locationName\":\"{locationName}\",\n" + + $"\"locationUrl\":\"{locationUrl}\",\n" + + $"\"hostUserId\":\"{hostUserId}\",\n" + + $"\"hostUserName\":\"{hostUserName}\",\n" + + $"\"timeTaken\":\"{timeTaken}\",\n" + + $"\"takeUserId\":\"{takeUserId}\",\n" + + $"\"takeUserName\":\"{takeUserName}\",\n" + + $"\"neosVersion\":\"{neosVersion}\",\n" + + $"\"takeUserName\":\"{takeUserName}\",\n" + + $"\"presentUserIdArray\":[\"{String.Join("\",\"", presentUserIdArray)}\"],\n" + + $"\"presentUserNameArray\":[\"{String.Join("\",\"", presentUserNameArray)}\"]}}"; + + byte[] header = { 0x55, 0x4E, 0x49, 0x43, 0x4F, 0x44, 0x45, 0x0 }; + byte[] content = System.Text.Encoding.Unicode.GetBytes(str); + byte[] main = new byte[header.Length + content.Length]; + Array.Copy(header, main, header.Length); + Array.Copy(content, 0, main, header.Length, content.Length); + w5.Len = main.Length; + w5.Value = main; + img.SetPropertyItem(w5); + + PropertyItem w6 = img.PropertyItems[0]; + w6.Id = 0x010E; //TITLE + w6.Type = 2; + w6.Len = 11; + w6.Value = System.Text.Encoding.ASCII.GetBytes("Neos Photo\0"); + img.SetPropertyItem(w6); + + PropertyItem w7 = img.PropertyItems[0]; + w7.Id = 0x013B; //ARTIST + w7.Type = 2; + var username = Engine.Current.LocalUserName; + w7.Len = username.Length + 1; + w7.Value = System.Text.Encoding.ASCII.GetBytes(username + "\0"); + img.SetPropertyItem(w7); + + PropertyItem w8 = img.PropertyItems[0]; + w8.Id = 0x0131; //ARTIST + w8.Type = 2; + w8.Len = 7; + w8.Value = System.Text.Encoding.ASCII.GetBytes("NeosVR\0"); + img.SetPropertyItem(w8); + + img.Save(str1); + img.Dispose(); + } + } + catch (Exception ex) + { + UniLog.Error("Exception saving screenshot to Windows:\n" + (object)ex); + } + finally + { + lib1::WindowsPlatformConnector.ScreenshotSemaphore.Release(); + } + })); + + + return false; + // __instance.ForEachConnector((Action)(c => Msg(c.GetType().Assembly))); + + //Msg(file + //string[] filename = file.Split('\\'); + //if (!filename[filename.Length - 1].Contains("jpg")) return; + //File.Move(file, file + "d"); + //Image img = Image.FromFile(file + "d"); + //System.Drawing.Imaging.PropertyItem w = img.PropertyItems[0]; + //Msg(w); + //w = img.PropertyItems[0]; + //SetProperty(ref w, 272, "NeosVR kokopi MOD"); + //img.SetPropertyItem(w); + //img.Save(file); + //img.Dispose(); + + // File.Copy(file, @"C:\Users\neo.KOKOA\Documents\k\" + filename[filename.Length - 1]); + // return true; + //FIBITMAP freeImage = __instance.ToFreeImage(); + //MetadataTag tag = new MetadataTag(FREE_IMAGE_MDMODEL.FIMD_COMMENTS); + //tag = new MetadataTag(FREE_IMAGE_MDMODEL.FIMD_COMMENTS); + //tag.Key = "KEY1"; + //tag.Value = 12345; + //tag.AddToImage(freeImage); + + //tag = new MetadataTag(FREE_IMAGE_MDMODEL.FIMD_COMMENTS); + //tag.Key = "KEY2"; + //tag.Value = 54321; + //tag.AddToImage(freeImage); + //Msg("ok"); + //Msg(extension); + //try + //{ + // __result = TextureEncoder.Encode(freeImage, stream, extension, quality, preserveColorInAlpha); + //} + //finally + //{ + // FreeImage.Unload(freeImage); + //} + //return false; + // Msg(FrooxEngine.Engine.Current.WorldManager.FocusedWorld.Name); + } + } + } +} \ No newline at end of file diff --git a/SaveExif/SaveExif.csproj b/SaveExif/SaveExif.csproj new file mode 100644 index 0000000..0d31654 --- /dev/null +++ b/SaveExif/SaveExif.csproj @@ -0,0 +1,85 @@ + + + + + Debug + AnyCPU + {39BD8749-F45A-4623-97F3-0572E74ED463} + Library + Properties + SaveExif + SaveExif + v4.7.2 + 512 + true + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\NeosVR\nml_libs\0Harmony.dll + + + ..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\NeosVR\Neos_Data\Managed\Assembly-CSharp.dll + lib1 + + + ..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\NeosVR\Neos_Data\Managed\BaseX.dll + + + ..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\NeosVR\Neos_Data\Managed\CloudX.Shared.dll + + + ..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\NeosVR\Neos_Data\Managed\CodeX.dll + + + False + ..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\NeosVR\Neos_Data\Managed\FreeImageNET.dll + + + ..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\NeosVR\Neos_Data\Managed\FrooxEngine.dll + + + False + ..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\NeosVR\Neos_Data\Managed\MimeDetective.dll + + + ..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\NeosVR\Libraries\NeosModLoader.dll + + + + + + + + + + + + + + + + + + + + copy "$(TargetDir)\$(TargetFileName)" "C:\Neos\app\nml_mods\" + + \ No newline at end of file