├── .github
└── workflows
│ └── main.yml
├── .gitignore
├── .readme
├── InGameTranslation.png
└── TranslationFolder.png
├── DSPTranslationPlugin.sln
├── DSPTranslationPlugin.sln.DotSettings
├── DSPTranslationPlugin
├── DSPTranslationPlugin.csproj
├── DSPTranslationPlugin.csproj.DotSettings
├── GameHarmony
│ ├── GameOption_Apply_Harmony.cs
│ ├── GlobalObject_Initialize_Harmony.cs
│ ├── GlobalObject_SaveLastLanguage_Harmony.cs
│ ├── Localizer_Refresh_Harmony.cs
│ ├── ManualBehaviour_All_Harmony.cs
│ ├── StringTranslate_Translate_Harmony.cs
│ ├── TranslationFix
│ │ └── UIReplicatorWindow_OnUpdate_Harmony.cs
│ ├── UIOptionWindow_TempOptionToUI_Harmony.cs
│ ├── UIOptionWindow_UIToTempOption_Harmony.cs
│ └── VFPreload_PreloadThread_Harmony.cs
├── TranslationPlugin.cs
└── UnityHarmony
│ ├── Text_Font_Getter_Harmony.cs
│ ├── UIBehaviour_Awake.cs
│ └── UIBehaviour_OnDestroy.cs
├── LICENSE
├── NuGet.Config
├── README.md
├── Tests
├── ReflectionTests.cs
├── SimpleJSON
│ ├── ComplexDataTypes.cs
│ ├── SerializeFirstAsObjectTypes.cs
│ ├── SimpleJSONTests.cs
│ └── UnityTypes.cs
├── Tests.csproj
└── UIFixes
│ ├── FromJsonTests.cs
│ ├── RangeValueTests.cs
│ ├── SimpleUIFixesTests.cs
│ └── TestableUIBehaviourComponent.cs
├── TranslationCommon
├── ConsoleLogger.cs
├── Fonts
│ ├── CustomFontData.cs
│ ├── Fixes
│ │ ├── IBehaviourComponent.cs
│ │ ├── IFix.cs
│ │ ├── RangeValue.cs
│ │ ├── RectFix.cs
│ │ ├── RelativeValue.cs
│ │ ├── TextFix.cs
│ │ ├── UIActionFixes.cs
│ │ ├── UIBehaviourCache.cs
│ │ ├── UIBehaviourComponent.cs
│ │ ├── UIFix.cs
│ │ ├── UIFixesData.cs
│ │ └── UIPathTarget.cs
│ ├── TextDefaultFont.cs
│ └── TextFontManager.cs
├── GameObjectsUtils.cs
├── SimpleJSON
│ ├── Attributes.cs
│ ├── Changelog.txt
│ ├── LICENSE
│ ├── README
│ ├── ReflectionUtils.cs
│ ├── SimpleJSON.cs
│ ├── SimpleJSONBinary.cs
│ ├── SimpleJSONBuilder.cs
│ ├── SimpleJSONDotNetTypes.cs
│ ├── SimpleJSONUnity.cs
│ └── SimpleJSONUtils.cs
├── Translation
│ ├── LanguageContainer.cs
│ ├── LanguageData.cs
│ ├── LanguageSettings.cs
│ ├── TranslationManager.cs
│ └── TranslationProto.cs
├── TranslationCommon.csproj
├── Utils.cs
└── WildcardMatch.cs
├── icon.png
├── manifest.json
└── thunderstore.toml
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: .NET Publish
2 |
3 | on:
4 | push:
5 | tags:
6 | - "*.*.*"
7 |
8 | jobs:
9 | build:
10 |
11 | runs-on: windows-latest
12 |
13 | steps:
14 | - uses: actions/checkout@v2
15 | with:
16 | fetch-depth: 0
17 |
18 | - name: Setup .NET
19 | uses: actions/setup-dotnet@v1
20 | with:
21 | dotnet-version: 6.0.x
22 | include-prerelease: true
23 |
24 | # Add MSBuild to the PATH: https://github.com/microsoft/setup-msbuild
25 | - name: Setup MSBuild.exe
26 | uses: microsoft/setup-msbuild@v1.0.3
27 |
28 | - name: Setup .NET Framework
29 | run: choco install netfx-4.8-devpack
30 |
31 | - name: Setup .NET SDK
32 | run: choco install dotnetcore-sdk
33 |
34 | - name: Restore dependencies
35 | run: dotnet restore
36 |
37 | - name: Build
38 | run: dotnet build DSPTranslationPlugin/DSPTranslationPlugin.csproj -c Release
39 |
40 | #- name: Test
41 | # run: dotnet test --no-build --verbosity normal
42 |
43 | - name: Archive Release
44 | uses: thedoctor0/zip-release@master
45 | with:
46 | type: 'zip'
47 | filename: 'DSPTranslationPlugin.zip'
48 | directory: 'DSPTranslationPlugin/bin/Release/net472/'
49 |
50 | - name: Publish to Github Releases
51 | uses: ncipollo/release-action@v1
52 | with:
53 | artifacts: "DSPTranslationPlugin/bin/Release/net472/DSPTranslationPlugin.zip"
54 | token: ${{ secrets.GITHUB_TOKEN }}
55 |
56 | - name: Publish to Thunderstore
57 | run: |
58 | powershell -Command "(New-Object Net.WebClient).DownloadFile('https://github.com/thunderstore-io/thunderstore-cli/releases/download/0.1.1/tcli-0.1.1-win-x64.zip', 'tcli.zip')"
59 | unzip -o tcli.zip
60 | ./tcli.exe publish --token ${{ secrets.THUNDERSTORE }} --file DSPTranslationPlugin/bin/Release/net472/DSPTranslationPlugin.zip
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Mono auto generated files
17 | mono_crash.*
18 |
19 | # Build results
20 | [Dd]ebug/
21 | [Dd]ebugPublic/
22 | [Rr]elease/
23 | [Rr]eleases/
24 | x64/
25 | x86/
26 | [Aa][Rr][Mm]/
27 | [Aa][Rr][Mm]64/
28 | bld/
29 | [Bb]in/
30 | [Oo]bj/
31 | [Ll]og/
32 | [Ll]ogs/
33 |
34 | # Visual Studio 2015/2017 cache/options directory
35 | .vs/
36 | # Uncomment if you have tasks that create the project's static files in wwwroot
37 | #wwwroot/
38 |
39 | # Visual Studio 2017 auto generated files
40 | Generated\ Files/
41 |
42 | # MSTest test Results
43 | [Tt]est[Rr]esult*/
44 | [Bb]uild[Ll]og.*
45 |
46 | # NUnit
47 | *.VisualState.xml
48 | TestResult.xml
49 | nunit-*.xml
50 |
51 | # Build Results of an ATL Project
52 | [Dd]ebugPS/
53 | [Rr]eleasePS/
54 | dlldata.c
55 |
56 | # Benchmark Results
57 | BenchmarkDotNet.Artifacts/
58 |
59 | # .NET Core
60 | project.lock.json
61 | project.fragment.lock.json
62 | artifacts/
63 |
64 | # StyleCop
65 | StyleCopReport.xml
66 |
67 | # Files built by Visual Studio
68 | *_i.c
69 | *_p.c
70 | *_h.h
71 | *.ilk
72 | *.meta
73 | *.obj
74 | *.iobj
75 | *.pch
76 | *.pdb
77 | *.ipdb
78 | *.pgc
79 | *.pgd
80 | *.rsp
81 | *.sbr
82 | *.tlb
83 | *.tli
84 | *.tlh
85 | *.tmp
86 | *.tmp_proj
87 | *_wpftmp.csproj
88 | *.log
89 | *.vspscc
90 | *.vssscc
91 | .builds
92 | *.pidb
93 | *.svclog
94 | *.scc
95 |
96 | # Chutzpah Test files
97 | _Chutzpah*
98 |
99 | # Visual C++ cache files
100 | ipch/
101 | *.aps
102 | *.ncb
103 | *.opendb
104 | *.opensdf
105 | *.sdf
106 | *.cachefile
107 | *.VC.db
108 | *.VC.VC.opendb
109 |
110 | # Visual Studio profiler
111 | *.psess
112 | *.vsp
113 | *.vspx
114 | *.sap
115 |
116 | # Visual Studio Trace Files
117 | *.e2e
118 |
119 | # TFS 2012 Local Workspace
120 | $tf/
121 |
122 | # Guidance Automation Toolkit
123 | *.gpState
124 |
125 | # ReSharper is a .NET coding add-in
126 | _ReSharper*/
127 | *.[Rr]e[Ss]harper
128 | *.DotSettings.user
129 |
130 | # TeamCity is a build add-in
131 | _TeamCity*
132 |
133 | # DotCover is a Code Coverage Tool
134 | *.dotCover
135 |
136 | # AxoCover is a Code Coverage Tool
137 | .axoCover/*
138 | !.axoCover/settings.json
139 |
140 | # Visual Studio code coverage results
141 | *.coverage
142 | *.coveragexml
143 |
144 | # NCrunch
145 | _NCrunch_*
146 | .*crunch*.local.xml
147 | nCrunchTemp_*
148 |
149 | # MightyMoose
150 | *.mm.*
151 | AutoTest.Net/
152 |
153 | # Web workbench (sass)
154 | .sass-cache/
155 |
156 | # Installshield output folder
157 | [Ee]xpress/
158 |
159 | # DocProject is a documentation generator add-in
160 | DocProject/buildhelp/
161 | DocProject/Help/*.HxT
162 | DocProject/Help/*.HxC
163 | DocProject/Help/*.hhc
164 | DocProject/Help/*.hhk
165 | DocProject/Help/*.hhp
166 | DocProject/Help/Html2
167 | DocProject/Help/html
168 |
169 | # Click-Once directory
170 | publish/
171 |
172 | # Publish Web Output
173 | *.[Pp]ublish.xml
174 | *.azurePubxml
175 | # Note: Comment the next line if you want to checkin your web deploy settings,
176 | # but database connection strings (with potential passwords) will be unencrypted
177 | *.pubxml
178 | *.publishproj
179 |
180 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
181 | # checkin your Azure Web App publish settings, but sensitive information contained
182 | # in these scripts will be unencrypted
183 | PublishScripts/
184 |
185 | # NuGet Packages
186 | *.nupkg
187 | # NuGet Symbol Packages
188 | *.snupkg
189 | # The packages folder can be ignored because of Package Restore
190 | **/[Pp]ackages/*
191 | # except build/, which is used as an MSBuild target.
192 | !**/[Pp]ackages/build/
193 | # Uncomment if necessary however generally it will be regenerated when needed
194 | #!**/[Pp]ackages/repositories.config
195 | # NuGet v3's project.json files produces more ignorable files
196 | *.nuget.props
197 | *.nuget.targets
198 |
199 | # Microsoft Azure Build Output
200 | csx/
201 | *.build.csdef
202 |
203 | # Microsoft Azure Emulator
204 | ecf/
205 | rcf/
206 |
207 | # Windows Store app package directories and files
208 | AppPackages/
209 | BundleArtifacts/
210 | Package.StoreAssociation.xml
211 | _pkginfo.txt
212 | *.appx
213 | *.appxbundle
214 | *.appxupload
215 |
216 | # Visual Studio cache files
217 | # files ending in .cache can be ignored
218 | *.[Cc]ache
219 | # but keep track of directories ending in .cache
220 | !?*.[Cc]ache/
221 |
222 | # Others
223 | ClientBin/
224 | ~$*
225 | *~
226 | *.dbmdl
227 | *.dbproj.schemaview
228 | *.jfm
229 | *.pfx
230 | *.publishsettings
231 | orleans.codegen.cs
232 |
233 | # Including strong name files can present a security risk
234 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
235 | #*.snk
236 |
237 | # Since there are multiple workflows, uncomment next line to ignore bower_components
238 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
239 | #bower_components/
240 |
241 | # RIA/Silverlight projects
242 | Generated_Code/
243 |
244 | # Backup & report files from converting an old project file
245 | # to a newer Visual Studio version. Backup files are not needed,
246 | # because we have git ;-)
247 | _UpgradeReport_Files/
248 | Backup*/
249 | UpgradeLog*.XML
250 | UpgradeLog*.htm
251 | ServiceFabricBackup/
252 | *.rptproj.bak
253 |
254 | # SQL Server files
255 | *.mdf
256 | *.ldf
257 | *.ndf
258 |
259 | # Business Intelligence projects
260 | *.rdl.data
261 | *.bim.layout
262 | *.bim_*.settings
263 | *.rptproj.rsuser
264 | *- [Bb]ackup.rdl
265 | *- [Bb]ackup ([0-9]).rdl
266 | *- [Bb]ackup ([0-9][0-9]).rdl
267 |
268 | # Microsoft Fakes
269 | FakesAssemblies/
270 |
271 | # GhostDoc plugin setting file
272 | *.GhostDoc.xml
273 |
274 | # Node.js Tools for Visual Studio
275 | .ntvs_analysis.dat
276 | node_modules/
277 |
278 | # Visual Studio 6 build log
279 | *.plg
280 |
281 | # Visual Studio 6 workspace options file
282 | *.opt
283 |
284 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
285 | *.vbw
286 |
287 | # Visual Studio LightSwitch build output
288 | **/*.HTMLClient/GeneratedArtifacts
289 | **/*.DesktopClient/GeneratedArtifacts
290 | **/*.DesktopClient/ModelManifest.xml
291 | **/*.Server/GeneratedArtifacts
292 | **/*.Server/ModelManifest.xml
293 | _Pvt_Extensions
294 |
295 | # Paket dependency manager
296 | .paket/paket.exe
297 | paket-files/
298 |
299 | # FAKE - F# Make
300 | .fake/
301 |
302 | # CodeRush personal settings
303 | .cr/personal
304 |
305 | # Python Tools for Visual Studio (PTVS)
306 | __pycache__/
307 | *.pyc
308 |
309 | # Cake - Uncomment if you are using it
310 | # tools/**
311 | # !tools/packages.config
312 |
313 | # Tabs Studio
314 | *.tss
315 |
316 | # Telerik's JustMock configuration file
317 | *.jmconfig
318 |
319 | # BizTalk build output
320 | *.btp.cs
321 | *.btm.cs
322 | *.odx.cs
323 | *.xsd.cs
324 |
325 | # OpenCover UI analysis results
326 | OpenCover/
327 |
328 | # Azure Stream Analytics local run output
329 | ASALocalRun/
330 |
331 | # MSBuild Binary and Structured Log
332 | *.binlog
333 |
334 | # NVidia Nsight GPU debugger configuration file
335 | *.nvuser
336 |
337 | # MFractors (Xamarin productivity tool) working folder
338 | .mfractor/
339 |
340 | # Local History for Visual Studio
341 | .localhistory/
342 |
343 | # BeatPulse healthcheck temp database
344 | healthchecksdb
345 |
346 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
347 | MigrationBackup/
348 |
349 | # Ionide (cross platform F# VS Code tools) working folder
350 | .ionide/
351 |
352 | #Rider IDE
353 | *.idea/*
--------------------------------------------------------------------------------
/.readme/InGameTranslation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muchaszewski/DSP_TranslationMod/bc74e3ae3500ce3c4fb86b7191a9cc5b9e56ee2f/.readme/InGameTranslation.png
--------------------------------------------------------------------------------
/.readme/TranslationFolder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muchaszewski/DSP_TranslationMod/bc74e3ae3500ce3c4fb86b7191a9cc5b9e56ee2f/.readme/TranslationFolder.png
--------------------------------------------------------------------------------
/DSPTranslationPlugin.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DSPTranslationPlugin", "DSPTranslationPlugin\DSPTranslationPlugin.csproj", "{31B05D90-5EFD-4902-827A-27CFD10D296D}"
4 | EndProject
5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TranslationCommon", "TranslationCommon\TranslationCommon.csproj", "{B89B56B6-D8B9-40E7-8D8B-B2501E138B5E}"
6 | EndProject
7 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{5025C0ED-5171-45CD-B631-34359778587D}"
8 | EndProject
9 | Global
10 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
11 | Debug|Any CPU = Debug|Any CPU
12 | Release|Any CPU = Release|Any CPU
13 | EndGlobalSection
14 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
15 | {31B05D90-5EFD-4902-827A-27CFD10D296D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
16 | {31B05D90-5EFD-4902-827A-27CFD10D296D}.Debug|Any CPU.Build.0 = Debug|Any CPU
17 | {31B05D90-5EFD-4902-827A-27CFD10D296D}.Release|Any CPU.ActiveCfg = Release|Any CPU
18 | {31B05D90-5EFD-4902-827A-27CFD10D296D}.Release|Any CPU.Build.0 = Release|Any CPU
19 | {B89B56B6-D8B9-40E7-8D8B-B2501E138B5E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
20 | {B89B56B6-D8B9-40E7-8D8B-B2501E138B5E}.Debug|Any CPU.Build.0 = Debug|Any CPU
21 | {B89B56B6-D8B9-40E7-8D8B-B2501E138B5E}.Release|Any CPU.ActiveCfg = Release|Any CPU
22 | {B89B56B6-D8B9-40E7-8D8B-B2501E138B5E}.Release|Any CPU.Build.0 = Release|Any CPU
23 | {5025C0ED-5171-45CD-B631-34359778587D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
24 | {5025C0ED-5171-45CD-B631-34359778587D}.Debug|Any CPU.Build.0 = Debug|Any CPU
25 | {5025C0ED-5171-45CD-B631-34359778587D}.Release|Any CPU.ActiveCfg = Release|Any CPU
26 | {5025C0ED-5171-45CD-B631-34359778587D}.Release|Any CPU.Build.0 = Release|Any CPU
27 | EndGlobalSection
28 | EndGlobal
29 |
--------------------------------------------------------------------------------
/DSPTranslationPlugin.sln.DotSettings:
--------------------------------------------------------------------------------
1 |
2 | True
3 | True
--------------------------------------------------------------------------------
/DSPTranslationPlugin/DSPTranslationPlugin.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net472
5 | DSPTranslationPlugin
6 | DSPTranslationPlugin
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | icon.png
16 | PreserveNewest
17 |
18 |
19 | LICENSE
20 | PreserveNewest
21 |
22 |
23 | manifest.json
24 | PreserveNewest
25 |
26 |
27 | README.md
28 | PreserveNewest
29 |
30 |
31 | thunderstore.toml
32 | PreserveNewest
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/DSPTranslationPlugin/DSPTranslationPlugin.csproj.DotSettings:
--------------------------------------------------------------------------------
1 |
2 | True
--------------------------------------------------------------------------------
/DSPTranslationPlugin/GameHarmony/GameOption_Apply_Harmony.cs:
--------------------------------------------------------------------------------
1 | using HarmonyLib;
2 | using TranslationCommon.Translation;
3 |
4 | namespace DSPTranslationPlugin.GameHarmony
5 | {
6 |
7 | [HarmonyPatch(typeof(GameOption), nameof(GameOption.Apply))]
8 | public static class GameOption_Apply_Harmony
9 | {
10 | ///
11 | /// Load current language after pressing "Apply" button
12 | ///
13 | [HarmonyPrefix]
14 | public static void Prefix(GameOption __instance)
15 | {
16 | TranslationManager.LoadCurrentLanguage();
17 | }
18 | }
19 | }
--------------------------------------------------------------------------------
/DSPTranslationPlugin/GameHarmony/GlobalObject_Initialize_Harmony.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using HarmonyLib;
3 | using TranslationCommon.Translation;
4 | using UnityEngine;
5 |
6 | namespace DSPTranslationPlugin.GameHarmony
7 | {
8 | [HarmonyPatch(typeof(GlobalObject), "Initialize")]
9 | public static class GlobalObject_Initialize_Harmony
10 | {
11 | ///
12 | /// Try settings previously saved language
13 | ///
14 | [HarmonyPrefix]
15 | public static void Prefix()
16 | {
17 | var result = PlayerPrefs.GetString(TranslationManager.PlayerPrefsCode);
18 | if (!String.IsNullOrEmpty(result))
19 | {
20 | TranslationManager.SelectedLanguage = result;
21 | TranslationManager.LoadCurrentLanguage();
22 | }
23 | }
24 |
25 | }
26 | }
--------------------------------------------------------------------------------
/DSPTranslationPlugin/GameHarmony/GlobalObject_SaveLastLanguage_Harmony.cs:
--------------------------------------------------------------------------------
1 | using HarmonyLib;
2 | using TranslationCommon.Translation;
3 | using UnityEngine;
4 |
5 | namespace DSPTranslationPlugin.GameHarmony
6 | {
7 | [HarmonyPatch(typeof(GlobalObject), nameof(GlobalObject.SaveLastLanguage))]
8 | public static class GlobalObject_SaveLastLanguage_Harmony
9 | {
10 | ///
11 | /// Save last used language
12 | ///
13 | [HarmonyPrefix]
14 | public static void Prefix()
15 | {
16 | if (!TranslationManager.IsInitialized)
17 | {
18 | TranslationManager.LoadCurrentLanguage();
19 | }
20 | else
21 | {
22 | if (TranslationManager.CurrentLanguage == null)
23 | {
24 | PlayerPrefs.DeleteKey(TranslationManager.PlayerPrefsCode);
25 | }
26 | else
27 | {
28 | PlayerPrefs.SetString(TranslationManager.PlayerPrefsCode, TranslationManager.CurrentLanguage.Settings.LanguageDisplayName);
29 | }
30 | PlayerPrefs.Save();
31 | }
32 | }
33 | }
34 | }
--------------------------------------------------------------------------------
/DSPTranslationPlugin/GameHarmony/Localizer_Refresh_Harmony.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using HarmonyLib;
4 | using UnityEngine;
5 | using UnityEngine.Networking;
6 | using UnityEngine.UI;
7 |
8 | namespace DSPTranslationPlugin.GameHarmony
9 | {
10 | ///
11 | /// Localizer postfix for expanding translation box to match new credits text
12 | ///
13 | [HarmonyPatch(typeof(Localizer), "Refresh")]
14 | public static class Localizer_Refresh_Harmony
15 | {
16 | [HarmonyPrefix]
17 | public static bool Prefix(Localizer __instance)
18 | {
19 | var maskableGraphics = __instance.GetComponents();
20 | __instance.translation = __instance.stringKey.Translate();
21 | foreach (var graphic in maskableGraphics)
22 | {
23 | if (graphic is Text text)
24 | {
25 | text.text = __instance.translation;
26 | }
27 | else if (graphic is Image image)
28 | {
29 | if (IsUri(__instance.translation))
30 | {
31 | __instance.StartCoroutine(GetRequest(__instance.translation, image));
32 | }
33 | else
34 | {
35 | image.sprite = Resources.Load(__instance.translation);
36 | }
37 | }
38 | else if (graphic is RawImage rawImage)
39 | {
40 | if (IsUri(__instance.translation))
41 | {
42 | __instance.StartCoroutine(GetRequest(__instance.translation, rawImage));
43 | }
44 | else
45 | {
46 | rawImage.texture = Resources.Load(__instance.translation);
47 | }
48 | }
49 | }
50 |
51 | return false;
52 | }
53 |
54 | private static bool IsUri(string translation)
55 | {
56 | return translation.StartsWith("http") || translation.StartsWith("file");
57 | }
58 |
59 | ///
60 | /// Expand in game credits box
61 | ///
62 | ///
63 | [HarmonyPostfix]
64 | public static void Postfix(Localizer __instance)
65 | {
66 | ExpandGameCreditsBox(__instance);
67 | }
68 |
69 | private static void ExpandGameCreditsBox(Localizer __instance)
70 | {
71 | if (__instance.name == "tip" && __instance.transform.parent.name == "language")
72 | {
73 | var rect = __instance.GetComponent();
74 | var sizeDelta = rect.sizeDelta;
75 | sizeDelta.x = 600;
76 | sizeDelta.y = 90;
77 | rect.sizeDelta = sizeDelta;
78 | }
79 | }
80 |
81 | private static IEnumerator GetRequest(string uri, RawImage image)
82 | {
83 | var www = UnityWebRequestTexture.GetTexture(uri);
84 | yield return www.SendWebRequest();
85 |
86 | if (www.isNetworkError || www.isHttpError)
87 | {
88 | Debug.LogError(www.error);
89 | }
90 | else
91 | {
92 | var myTexture = ((DownloadHandlerTexture) www.downloadHandler).texture;
93 | image.texture = myTexture;
94 | }
95 | }
96 |
97 | private static IEnumerator GetRequest(string uri, Image image)
98 | {
99 | var www = UnityWebRequestTexture.GetTexture(uri);
100 | yield return www.SendWebRequest();
101 |
102 | if (www.isNetworkError || www.isHttpError)
103 | {
104 | Debug.LogError(www.error);
105 | }
106 | else
107 | {
108 | var myTexture = ((DownloadHandlerTexture) www.downloadHandler).texture;
109 | var sprite = Sprite.Create(myTexture, new Rect(0, 0, myTexture.width, myTexture.height), Vector2.zero);
110 | image.sprite = sprite;
111 | }
112 | }
113 | }
114 | }
--------------------------------------------------------------------------------
/DSPTranslationPlugin/GameHarmony/ManualBehaviour_All_Harmony.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using HarmonyLib;
3 | using TranslationCommon.Fonts;
4 |
5 | namespace DSPTranslationPlugin.GameHarmony
6 | {
7 | [HarmonyPatch(typeof(ManualBehaviour))]
8 | public static class ManualBehaviour_All_Harmony
9 | {
10 | public static Dictionary BehaviourComponents =
11 | new Dictionary();
12 |
13 | [HarmonyPostfix]
14 | [HarmonyPatch("_Create")]
15 | public static void Postfix_OnCreate(ManualBehaviour __instance)
16 | {
17 | if (!BehaviourComponents.ContainsKey(__instance))
18 | {
19 | BehaviourComponents.Add(__instance, new UIBehaviourComponent(__instance));
20 | }
21 | BehaviourComponents[__instance].OnCreate();
22 | }
23 |
24 | [HarmonyPostfix]
25 | [HarmonyPatch("_Destroy")]
26 | public static void Postfix_OnDestroy(ManualBehaviour __instance)
27 | {
28 | BehaviourComponents.Remove(__instance);
29 | }
30 |
31 | [HarmonyPostfix]
32 | [HarmonyPatch("_Init")]
33 | public static void Postfix_OnInit(ManualBehaviour __instance)
34 | {
35 | if (!BehaviourComponents.ContainsKey(__instance))
36 | {
37 | BehaviourComponents.Add(__instance, new UIBehaviourComponent(__instance));
38 | }
39 | BehaviourComponents[__instance].OnInit();
40 | }
41 |
42 | /*[HarmonyPostfix]
43 | [HarmonyPatch("_Open")]
44 | public static void Postfix_OnOpen(ManualBehaviour __instance)
45 | {
46 | if (!BehaviourComponents.ContainsKey(__instance))
47 | {
48 | BehaviourComponents.Add(__instance, new UIBehaviourComponent(__instance));
49 | }
50 |
51 | BehaviourComponents[__instance].OnOpen();
52 | }
53 |
54 | [HarmonyPostfix]
55 | [HarmonyPatch("_Update")]
56 | public static void Postfix_OnUpdate(ManualBehaviour __instance)
57 | {
58 | if (__instance.isActiveAndEnabled)
59 | {
60 | if (!BehaviourComponents.ContainsKey(__instance))
61 | {
62 | BehaviourComponents.Add(__instance, new UIBehaviourComponent(__instance));
63 | }
64 |
65 | BehaviourComponents[__instance].OnUpdate();
66 | }
67 | }
68 |
69 | [HarmonyPostfix]
70 | [HarmonyPatch("_LateUpdate")]
71 | public static void Postfix_OnLateUpdate(ManualBehaviour __instance)
72 | {
73 | if (__instance.isActiveAndEnabled)
74 | {
75 | if (!BehaviourComponents.ContainsKey(__instance))
76 | {
77 | BehaviourComponents.Add(__instance, new UIBehaviourComponent(__instance));
78 | }
79 | BehaviourComponents[__instance].OnLateUpdate();
80 | }
81 | }*/
82 | }
83 | }
--------------------------------------------------------------------------------
/DSPTranslationPlugin/GameHarmony/StringTranslate_Translate_Harmony.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using HarmonyLib;
3 | using TranslationCommon.Translation;
4 |
5 | namespace DSPTranslationPlugin.GameHarmony
6 | {
7 | [HarmonyPatch(typeof(StringTranslate), nameof(StringTranslate.Translate), typeof(string))]
8 | public static class StringTranslate_Translate_Harmony
9 | {
10 | ///
11 | /// Translate requested text
12 | ///
13 | /// Returned result
14 | /// Input NAME text
15 | ///
16 | [HarmonyPrefix]
17 | public static bool Prefix(ref string __result, string s)
18 | {
19 | if (s == null)
20 | {
21 | return true;
22 | }
23 |
24 | if (TranslationManager.CurrentLanguage != null)
25 | {
26 | if (TranslationManager.TranslationDictionary.ContainsKey(s))
27 | {
28 | __result = TranslationManager.TranslationDictionary[s].Translation;
29 | return false;
30 | }
31 |
32 | return true;
33 | }
34 |
35 | return true;
36 | }
37 |
38 | ///
39 | /// Add in game credits
40 | ///
41 | ///
42 | ///
43 | [HarmonyPostfix]
44 | public static void Postfix(ref string __result, string s)
45 | {
46 | if (s == "需要重启完全生效")
47 | {
48 | __result +=
49 | "\nTranslation tool made by Muchaszewski with the help of community" +
50 | "\nhttps://github.com/Muchaszewski/DSP_TranslationMod";
51 | }
52 | }
53 | }
54 | }
--------------------------------------------------------------------------------
/DSPTranslationPlugin/GameHarmony/TranslationFix/UIReplicatorWindow_OnUpdate_Harmony.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Reflection;
3 | using System.Reflection.Emit;
4 | using HarmonyLib;
5 | using UnityEngine.UI;
6 |
7 | namespace DSPTranslationPlugin.GameHarmony.TranslationFix
8 | {
9 | public class UIReplicatorWindow_OnUpdate_Harmony
10 | {
11 | [HarmonyPatch(typeof(UIReplicatorWindow), "_OnUpdate")]
12 | public static class UIReplicatorWindow_OnUpdate_Prefix
13 | {
14 | private static bool isPatched = false;
15 |
16 | ///
17 | /// Fixed "Replicating Queue" text
18 | ///
19 | ///
20 | [HarmonyPrefix]
21 | public static void Prefix(UIReplicatorWindow __instance)
22 | {
23 | if (!isPatched)
24 | {
25 | var _tmp_text0 = AccessTools.Field(typeof(UIReplicatorWindow), "_tmp_text0");
26 | // 制造队列 == "Replicating Queue"
27 | _tmp_text0.SetValue(__instance, "制造队列".Translate());
28 | isPatched = true;
29 | }
30 | }
31 | }
32 | }
33 | }
--------------------------------------------------------------------------------
/DSPTranslationPlugin/GameHarmony/UIOptionWindow_TempOptionToUI_Harmony.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Reflection;
5 | using DSPTranslationPlugin.UnityHarmony;
6 | using HarmonyLib;
7 | using TranslationCommon;
8 | using TranslationCommon.Translation;
9 | using UnityEngine;
10 | using UnityEngine.UI;
11 | using Object = UnityEngine.Object;
12 |
13 | namespace DSPTranslationPlugin.GameHarmony
14 | {
15 | [HarmonyPatch(typeof(UIOptionWindow), "TempOptionToUI")]
16 | public static class UIOptionWindow_TempOptionToUI_Harmony
17 | {
18 | private static int? _originalUICount;
19 |
20 | private static UIComboBox[] languageComboBoxes;
21 |
22 | private static string[] InGameFonts = new[]
23 | {
24 | "Default",
25 | };
26 |
27 | [HarmonyPrefix]
28 | public static void Prefix(UIOptionWindow __instance)
29 | {
30 | AddComboBoxLanguage(__instance);
31 | }
32 |
33 | [HarmonyPostfix]
34 | public static void Postfix(UIOptionWindow __instance)
35 | {
36 | ApplyComboBoxLanguage(__instance);
37 | CreateFontCompoBox(__instance);
38 | }
39 |
40 | ///
41 | /// Create option combobox with selection of font
42 | ///
43 | private static void CreateFontCompoBox(UIOptionWindow __instance)
44 | {
45 | var parent = __instance.languageComp.transform.parent.parent;
46 | if (_originalUICount == null)
47 | {
48 | _originalUICount = parent.childCount;
49 | }
50 |
51 | var genericComboBox = __instance.tipLevelComp.transform.parent;
52 |
53 | if (languageComboBoxes == null)
54 | languageComboBoxes = new UIComboBox[InGameFonts.Length];
55 |
56 | // Needs to initialize UI
57 | if (_originalUICount == parent.childCount)
58 | {
59 | Localization.OnLanguageChange += ReloadFontsComboBox;
60 |
61 | for (int i = 0; i < InGameFonts.Length; i++)
62 | {
63 | var fontName = InGameFonts[i];
64 | // Add new combobox
65 | var root = Object.Instantiate(genericComboBox,
66 | genericComboBox.parent);
67 |
68 | root.gameObject.SetActive(true);
69 | Object.Destroy(root.GetComponent());
70 | root.GetComponent().text = $"Font - {fontName}";
71 |
72 | languageComboBoxes[i] = root.GetComponentInChildren();
73 | UIComboBox languageComboBox = languageComboBoxes[i];
74 | languageComboBox.Items.Clear();
75 | languageComboBox.Items.Add(fontName);
76 | foreach (var installedFont in TextFontManager.InstalledFonts)
77 | {
78 | languageComboBox.Items.Add(installedFont);
79 | }
80 |
81 |
82 | languageComboBox.text = fontName;
83 |
84 | var settingsIndex = TextFontManager.GetSettingIndex(fontName);
85 | ConsoleLogger.LogWarning("TextFontManager.GetSettingIndex " + settingsIndex);
86 | languageComboBox.itemIndex = settingsIndex;
87 |
88 | languageComboBox.onItemIndexChange.AddListener(() =>
89 | {
90 | var selectedFontName = languageComboBox.Items[languageComboBox.itemIndex];
91 | if (selectedFontName == fontName)
92 | {
93 | TextFontManager.RestoreDefaultFont(fontName);
94 | }
95 | else
96 | {
97 | Font font;
98 | try
99 | {
100 | if (TranslationManager.CurrentLanguage == null || TranslationManager.CurrentLanguage.Fonts == null)
101 | throw new InvalidOperationException();
102 |
103 | font = TranslationManager.CurrentLanguage.Fonts.First(font1 => font1.name == selectedFontName);
104 | }
105 | catch (InvalidOperationException)
106 | {
107 | font = Font.CreateDynamicFontFromOSFont(selectedFontName, 12);
108 | }
109 |
110 | TextFontManager.ApplyCustomFont(fontName, font);
111 | }
112 | });
113 |
114 | // Set Option position
115 | var rectTransform = root.GetComponent();
116 | var childCountWithoutRestore = __instance.languageComp.transform.parent.parent.childCount - 2;
117 | var position = __instance.languageComp.transform.parent.GetComponent().anchoredPosition;
118 | var offset = 40;
119 | rectTransform.anchoredPosition = new Vector2(position.x, position.y - offset * childCountWithoutRestore);
120 | }
121 | }
122 | }
123 |
124 | public static void ReloadFontsComboBox(Language lang)
125 | {
126 | for (int index = 0; index < InGameFonts.Length; index++)
127 | {
128 | UIComboBox languageComboBox = languageComboBoxes[index];
129 | if (languageComboBox == null)
130 | {
131 | return;
132 | }
133 | var fontName = InGameFonts[index];
134 | languageComboBox.Items.Clear();
135 | languageComboBox.Items.Add(fontName);
136 | foreach (var installedFont in TextFontManager.InstalledFonts)
137 | {
138 | languageComboBox.Items.Add(installedFont);
139 | }
140 | }
141 | }
142 |
143 | private static void AddComboBoxLanguage(UIOptionWindow __instance)
144 | {
145 | if (!__instance.languageComp.Items.Contains("(Original) Française"))
146 | {
147 | __instance.languageComp.Items.Add("(Original) Française");
148 | }
149 |
150 | foreach (var langauge in TranslationManager.Langauges)
151 | {
152 | if (!__instance.languageComp.Items.Contains(langauge.Settings.LanguageDisplayName))
153 | {
154 | __instance.languageComp.Items.Add(langauge.Settings.LanguageDisplayName);
155 | }
156 | }
157 | }
158 |
159 | private static void ApplyComboBoxLanguage(UIOptionWindow __instance)
160 | {
161 | if (TranslationManager.CurrentLanguage != null)
162 | {
163 | for (int i = 0; i < TranslationManager.Langauges.Count; i++)
164 | {
165 | if (TranslationManager.CurrentLanguage.Settings.LanguageDisplayName ==
166 | TranslationManager.Langauges[i].Settings.LanguageDisplayName)
167 | __instance.languageComp.itemIndex = 3 + i;
168 | }
169 | }
170 | }
171 | }
172 | }
--------------------------------------------------------------------------------
/DSPTranslationPlugin/GameHarmony/UIOptionWindow_UIToTempOption_Harmony.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using HarmonyLib;
3 | using TranslationCommon.Translation;
4 | using UnityEngine;
5 |
6 | namespace DSPTranslationPlugin.GameHarmony
7 | {
8 | [HarmonyPatch(typeof(UIOptionWindow), "UIToTempOption")]
9 | public static class UIOptionWindow_UIToTempOption_Harmony
10 | {
11 | [HarmonyPrefix]
12 | public static void Prefix(UIOptionWindow __instance)
13 | {
14 | UIComboBox comboBox = __instance.languageComp;
15 | var item = comboBox.itemIndex != -1
16 | ? comboBox.Items[comboBox.itemIndex]
17 | : "English";
18 | TranslationManager.SelectedLanguage = item;
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/DSPTranslationPlugin/GameHarmony/VFPreload_PreloadThread_Harmony.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Reflection;
3 | using HarmonyLib;
4 | using TranslationCommon;
5 | using TranslationCommon.SimpleJSON;
6 | using TranslationCommon.Translation;
7 |
8 | namespace DSPTranslationPlugin.GameHarmony
9 | {
10 | [HarmonyPatch(typeof(VFPreload), "PreloadThread")]
11 | public class VFPreload_PreloadThread
12 | {
13 | [HarmonyPostfix]
14 | public static void Postfix(VFPreload __instance)
15 | {
16 | var stringProtoSet = LDB.strings;
17 | foreach (var languageContainer in TranslationManager.Langauges)
18 | {
19 | var templateLanguageData = new LanguageData(languageContainer.Settings, stringProtoSet);
20 | languageContainer.LoadTranslation(templateLanguageData);
21 | }
22 | }
23 |
24 | private static ProtoSet GetProtoSet()
25 | where T : Proto
26 | {
27 | var properties = typeof(LDB).GetProperties(BindingFlags.Static | BindingFlags.Public);
28 | foreach (var property in properties)
29 | {
30 | var value = property.GetValue(null, null);
31 | var type = value.GetType();
32 | var dataArray = (Proto[])type.GetField("dataArray").GetValue(value);
33 | var arrayType = dataArray.GetType().GetElementType();
34 |
35 | if (arrayType != typeof(T))
36 | {
37 | continue;
38 | }
39 |
40 | return (ProtoSet)value;
41 | }
42 |
43 | return null;
44 | }
45 |
46 | private static void ProtoJsonDump()
47 | {
48 | var properties = typeof(LDB).GetProperties(BindingFlags.Static | BindingFlags.Public);
49 | foreach (var property in properties)
50 | {
51 | var value = property.GetValue(null, null);
52 | var type = value.GetType();
53 | var result = JSON.ToJson(value, true);
54 | var instanceGenericName = type.FullName;
55 | TextWriter writer = new StreamWriter($"{Utils.ConfigPath}/JsonDump/{instanceGenericName}.json");
56 | writer.Write(result);
57 | writer.Flush();
58 | writer.Close();
59 | ConsoleLogger.LogInfo("JsonProtoDumpSet: " + value + " of " + instanceGenericName);
60 | }
61 | }
62 | }
63 | }
--------------------------------------------------------------------------------
/DSPTranslationPlugin/TranslationPlugin.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Reflection;
6 | using BepInEx;
7 | using DSPTranslationPlugin.UnityHarmony;
8 | using HarmonyLib;
9 | using UnityEngine;
10 |
11 | namespace DSPTranslationPlugin
12 | {
13 | [BepInPlugin("com.muchaszewski.dsp_translationPlugin", "DSP Community Translation", "0.5.2")]
14 | public class TranslationPlugin : BaseUnityPlugin
15 | {
16 | public static MonoBehaviour StaticMonoBehaviour { get; private set; }
17 |
18 | private void Awake()
19 | {
20 | StaticMonoBehaviour = this;
21 | Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly());
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/DSPTranslationPlugin/UnityHarmony/Text_Font_Getter_Harmony.cs:
--------------------------------------------------------------------------------
1 | using HarmonyLib;
2 | using UnityEngine.UI;
3 |
4 | namespace DSPTranslationPlugin.UnityHarmony
5 | {
6 | ///
7 | /// Text harmony patcher to apply font to all requesting elements
8 | ///
9 | [HarmonyPatch(typeof(Text), nameof(Text.font), MethodType.Getter)]
10 | public static class Text_Font_Getter_Harmony
11 | {
12 | [HarmonyPrefix]
13 | public static void Prefix(Text __instance)
14 | {
15 | TextFontManager.Get(__instance)?.OnGetFont();
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/DSPTranslationPlugin/UnityHarmony/UIBehaviour_Awake.cs:
--------------------------------------------------------------------------------
1 | using HarmonyLib;
2 | using UnityEngine.EventSystems;
3 | using UnityEngine.UI;
4 |
5 | namespace DSPTranslationPlugin.UnityHarmony
6 | {
7 | [HarmonyPatch(typeof(UIBehaviour), "Awake")]
8 | public static class UIBehaviour_Awake
9 | {
10 | ///
11 | /// Awake method for Text to add TextFontManger
12 | ///
13 | ///
14 | [HarmonyPrefix]
15 | public static void Prefix(UIBehaviour __instance)
16 | {
17 | if (__instance is Text text)
18 | {
19 | TextFontManager.Add(text);
20 | }
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/DSPTranslationPlugin/UnityHarmony/UIBehaviour_OnDestroy.cs:
--------------------------------------------------------------------------------
1 | using HarmonyLib;
2 | using UnityEngine.EventSystems;
3 | using UnityEngine.UI;
4 |
5 | namespace DSPTranslationPlugin.UnityHarmony
6 | {
7 | [HarmonyPatch(typeof(UIBehaviour), "OnDestroy")]
8 | public static class UIBehaviour_OnDestroy
9 | {
10 | ///
11 | /// OnDestroy method for Text to remove text from TextFontManger
12 | ///
13 | ///
14 | [HarmonyPrefix]
15 | public static void Prefix(UIBehaviour __instance)
16 | {
17 | if (__instance is Text text)
18 | {
19 | TextFontManager.Remove(text);
20 | }
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Creative Commons Attribution-ShareAlike 4.0 International License
2 | This is a human-readable summary of the full license below.
3 | Under this license, you are free to:
4 |
5 | Share — copy and redistribute the material in any medium or format
6 | Adapt — remix, transform, and build upon the material for any purpose, even commercially.
7 | The licensor cannot revoke these freedoms as long as you follow the license terms.
8 |
9 | License terms:
10 |
11 | Attribution — You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use.
12 | ShareAlike — If you remix, transform, or build upon the material, you must distribute your contributions under the same license as the original.
13 | No additional restrictions — You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits.
14 | Notices:
15 |
16 | You do not have to comply with the license for elements of the material in the public domain or where your use is permitted by an applicable exception or limitation.
17 | No warranties are given. The license may not give you all of the permissions necessary for your intended use. For example, other rights such as publicity, privacy, or moral rights may limit how you use the material.
18 |
19 | Licence holder: https://github.com/Muchaszewski
20 |
21 | Creative Commons Attribution 4.0 International Public License - (Read Online) https://creativecommons.org/licenses/by/4.0/legalcode
22 | By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions.
23 |
24 | Section 1 – Definitions.
25 |
26 | Adapted Material means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image.
27 | Adapter's License means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License.
28 | Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights.
29 | Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements.
30 | Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material.
31 | Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License.
32 | Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license.
33 | Licensor means the individual(s) or entity(ies) granting rights under this Public License.
34 | Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them.
35 | Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world.
36 | You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning.
37 | Section 2 – Scope.
38 |
39 | License grant.
40 | Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to:
41 | reproduce and Share the Licensed Material, in whole or in part; and
42 | produce, reproduce, and Share Adapted Material.
43 | Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions.
44 | Term. The term of this Public License is specified in Section 6(a).
45 | Media and formats; technical modifications allowed. The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a)(4) never produces Adapted Material.
46 | Downstream recipients.
47 | Offer from the Licensor – Licensed Material. Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License.
48 | No downstream restrictions. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material.
49 | No endorsement. Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i).
50 | Other rights.
51 |
52 | Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise.
53 | Patent and trademark rights are not licensed under this Public License.
54 | To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties.
55 | Section 3 – License Conditions.
56 |
57 | Your exercise of the Licensed Rights is expressly made subject to the following conditions.
58 |
59 | Attribution.
60 |
61 | If You Share the Licensed Material (including in modified form), You must:
62 |
63 | retain the following if it is supplied by the Licensor with the Licensed Material:
64 | identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated);
65 | a copyright notice;
66 | a notice that refers to this Public License;
67 | a notice that refers to the disclaimer of warranties;
68 | a URI or hyperlink to the Licensed Material to the extent reasonably practicable;
69 | indicate if You modified the Licensed Material and retain an indication of any previous modifications; and
70 | indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License.
71 | You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information.
72 | If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable.
73 | If You Share Adapted Material You produce, the Adapter's License You apply must not prevent recipients of the Adapted Material from complying with this Public License.
74 | Section 4 – Sui Generis Database Rights.
75 |
76 | Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material:
77 |
78 | for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database;
79 | if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material; and
80 | You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database.
81 | For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights.
82 | Section 5 – Disclaimer of Warranties and Limitation of Liability.
83 |
84 | Unless otherwise separately undertaken by the Licensor, to the extent possible, the Licensor offers the Licensed Material as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not known or discoverable. Where disclaimers of warranties are not allowed in full or in part, this disclaimer may not apply to You.
85 | To the extent possible, in no event will the Licensor be liable to You on any legal theory (including, without limitation, negligence) or otherwise for any direct, special, indirect, incidental, consequential, punitive, exemplary, or other losses, costs, expenses, or damages arising out of this Public License or use of the Licensed Material, even if the Licensor has been advised of the possibility of such losses, costs, expenses, or damages. Where a limitation of liability is not allowed in full or in part, this limitation may not apply to You.
86 | The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability.
87 | Section 6 – Term and Termination.
88 |
89 | This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically.
90 | Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates:
91 |
92 | automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or
93 | upon express reinstatement by the Licensor.
94 | For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License.
95 | For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License.
96 | Sections 1, 5, 6, 7, and 8 survive termination of this Public License.
97 | Section 7 – Other Terms and Conditions.
98 |
99 | The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed.
100 | Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License.
101 | Section 8 – Interpretation.
102 |
103 | For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License.
104 | To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions.
105 | No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor.
106 | Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority.
--------------------------------------------------------------------------------
/NuGet.Config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [Support my work on Patreon](https://www.patreon.com/muchaszewski?fan_landing=true)
2 | # Dyson sphere translation plugin!
3 |
4 | [Licence: Project is under CC Attribution 4.0](https://raw.githubusercontent.com/Muchaszewski/DSP_TranslationMod/main/LICENSE)
5 |
6 | # BREAKING CHANGE
7 | New translation folder location for translation: `{Game Directory}\DSPGAME_Data\Translation\{LanguageName}\...`
8 |
9 | # Features
10 | - Adds possibility to add custom languages
11 | - Adds (currently hidden) French language
12 | - Adds the possibility to change Font in game (WORK IN PROGRESS)
13 | - Use custom logo for custom translation
14 |
15 | # Roadmap
16 | - Refactor code and add documentation
17 | - Add support for adjusting content size of in game UI to fit new text
18 |
19 | ## Installation via Mod manager
20 |
21 | [Download mod manager](https://dsp.thunderstore.io/package/ebkr/r2modman_dsp/)
22 |
23 | 1. Press Install with Mod Manager at https://dsp.thunderstore.io/package/Muchaszewski/DSPTranslationPlugin/
24 | 2. Add translations to
25 | `{Game Directory}\DSPGAME_Data\Translation\{LanguageName}\translation_DysonSphereProgram.json`.
26 | You can find [translations at Crowdin](https://crowdin.com/translate/dyson-sphere-program)
27 | 6. Select new translation in Menu of the Game
28 | 7. Enjoy
29 |
30 | ## Installation Manual
31 | 1. Download and unpack [BepInEx](https://github.com/BepInEx/BepInEx/releases) into game root directory
32 | 2. Download [mod files](https://github.com/Muchaszewski/DSP_TranslationMod/releases)
33 | 3. Extract zip file
34 | 4. Paste DLLs under `Dyson Sphere Program\BepInEx\plugins\DSPTranslationPlugin`
35 | 5. Add translations to
36 | `{Game Directory}\DSPGAME_Data\Translation\{LanguageName}\translation_DysonSphereProgram.json`.
37 | You can find [translations at Crowdin](https://crowdin.com/translate/dyson-sphere-program)
38 |
39 | 6. Select new translation in Menu of the Game
40 | 7. Enjoy
41 |
42 | 
43 |
44 | ## How to add new translations
45 | 1. Create folder under `Dyson Sphere Program\BepInEx\plugins\DSPTranslationPlugin\Translation` with the name of your translation eg: `Polish`
46 |
47 | 
48 |
49 | 2. Run game once - New file settings and translations files will be created.
50 | 3. Translate
51 |
52 | ### Translation file structure:
53 | ```
54 | { #CROWDIN
55 | "点击鼠标建造": "Click to build",
56 | "无法在此建造": "Cannot build here",
57 | "{NAME}: "{TRANSLATION}",
58 | (...)
59 | }
60 | ```
61 |
62 |
63 | ```
64 | { #LEGACY
65 | "TranslationTable": [
66 | {
67 | "IsValid": true, # Does translation exists in the game
68 | "Name": "点击鼠标建造", # Name used by the game for translation (READ ONLY)
69 | "Original": "Click to build", # Translation in English
70 | "Translation": "Kliknij, aby zbudować" # Your translation here
71 | },
72 | (...)
73 | ]
74 | }
75 | ```
76 |
77 | ### Settings file structure:
78 | ```
79 | {
80 | "Version": "0.1.0.0", # Plugin version
81 | "GameVersion": "0.6.15.5678", # Game version
82 | "OriginalLanguage": "ENUS", # Language in which empty new translation files will be generated, possible values: "ENUS", "FRFR", "ZHCN"
83 | "LanguageDisplayName": "Polish", # Language display name in the game
84 | "ImportFromLegacy": false, # Generate Legacy json format (defualt is Crowdin format)
85 | "CreateAndUpdateFromPlainTextDumpUnsafe": true # Should create and import dump file (more below)
86 | }
87 | ```
88 |
89 | ### Custom images:
90 | Currently in the game there are 2 images that can be changed, they look like a path - `UI/Textures/dsp-logo-en`. You can use a valid path from resources or valid URL.
91 | URL needs to be a direct png file. Eg:
92 | ```
93 | Internet URI -- "ImageLogo0": "https://wiki.factorio.com/images/thumb/Factorio-logo.png/461px-Factorio-logo.png",
94 | Local file URI -- "ImageLogo0": "file://C:/Users/Muchaszewski/Documents/Icon.png"
95 | ```
96 |
97 | #### Specific images description:
98 |
99 | - `ImageLogo0` and `ImageLogo1` needs to have aspect ratio that corresponds to 800x300 pixels, otherwise they will be stretched
100 |
101 | # Build from source
102 |
103 | 1. Download repository
104 | 2. Edit csproj reference to point to your game location
105 | 3.
106 |
107 | ## Run from DSPGAME.exe without steam
108 | In order to start the game without steam launcher create steam_appid.txt file in root game folder with `1366540` in it.
109 | (Steam launcher might be required to run in the background)
110 |
111 |
112 | # Special Thanks to
113 | [BepInEx](https://github.com/BepInEx/BepInEx/releases) - for mod support
114 |
115 | Modified hard fork of [SimpleJSON](https://github.com/Bunny83/SimpleJSON) - for simple json parser
116 |
--------------------------------------------------------------------------------
/Tests/ReflectionTests.cs:
--------------------------------------------------------------------------------
1 | using NUnit.Framework;
2 | using TranslationCommon.Translation;
3 | using Microsoft.VisualStudio.TestTools.UnitTesting;
4 | using Assert = Microsoft.VisualStudio.TestTools.UnitTesting.Assert;
5 |
6 |
7 | namespace Tests
8 | {
9 | public class ReflectionTests
10 | {
11 | public class StringProto
12 | {
13 | public string ZHCN;
14 | public string ENUS;
15 | public string FRFR;
16 | }
17 |
18 | [Test]
19 | public void GetTextFromStringProtoTest()
20 | {
21 | var settings = new LanguageSettings();
22 | settings.OriginalLanguage = "ENUS";
23 | var val = new StringProto()
24 | {
25 | ENUS = "ENUS",
26 | FRFR = "FRFR",
27 | ZHCN = "ZHCN",
28 | };
29 |
30 | var translationDelegate = LanguageData.GetOriginalTextDelegate(settings);
31 | Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual("ENUS", translationDelegate(val));
32 | }
33 | }
34 | }
--------------------------------------------------------------------------------
/Tests/SimpleJSON/ComplexDataTypes.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace Tests
6 | {
7 | public class ComplexDataTypes
8 | {
9 | public bool BoolField;
10 | public byte ByteField;
11 | public short ShortField;
12 | public ushort UshortField;
13 | public int IntField;
14 | public uint UintField;
15 | public char CharField;
16 | public float FloatField;
17 | public double DoubleField;
18 | public long LongField;
19 | public ulong UlongField;
20 | public decimal DecimalField;
21 | public sbyte SbyteField;
22 |
23 | public string StringField;
24 |
25 | public object UnknownTypeField;
26 | public ComplexDataTypes ParentField;
27 | public ComplexDataTypes ChildField;
28 |
29 | public List ListTypeField;
30 | public List ListStringField;
31 | public List ListIntField;
32 |
33 | public Dictionary StringStringDictionaryField;
34 | public Dictionary IntIntDictionaryField;
35 | public Dictionary ComplexComplexDictionaryField;
36 |
37 | public static ComplexDataTypes GenerateRandomValues(ComplexDataTypes parent = null, bool generateLoop = true)
38 | {
39 | ComplexDataTypes value = new ComplexDataTypes();
40 | var random = new Random(0);
41 | value.BoolField = random.NextDouble() > 0.5 ? true : false;
42 | value.ByteField = (byte) random.Next();
43 | value.CharField = (char) random.Next();
44 | value.ShortField = (short) random.Next();
45 | value.UshortField = (ushort) random.Next();
46 | value.IntField = random.Next();
47 | value.UintField = (uint) random.Next();
48 | value.FloatField = (float) random.NextDouble();
49 | value.DoubleField = random.NextDouble();
50 | value.LongField = (long) random.Next();
51 | value.UlongField = (ulong) random.Next();
52 | value.DecimalField = (decimal) random.NextDouble();
53 | value.SbyteField = (sbyte) random.Next();
54 |
55 | value.StringField = RandomStringGenerator.RandomString(8);
56 |
57 | value.UnknownTypeField = new object();
58 |
59 | if (generateLoop)
60 | {
61 | value.ParentField = parent;
62 | }
63 | value.ChildField = parent == null ? GenerateRandomValues(value, generateLoop) : null;
64 |
65 | if (parent == null)
66 | {
67 | value.ListTypeField = new List();
68 | for (int i = 0; i < 3; i++)
69 | {
70 | value.ListTypeField.Add(GenerateRandomValues(value, generateLoop));
71 | }
72 | }
73 | value.ListStringField = new List();
74 | for (int i = 0; i < 3; i++)
75 | {
76 | value.ListStringField.Add(RandomStringGenerator.RandomString(8));
77 | }
78 | value.ListIntField = new List();
79 | for (int i = 0; i < 3; i++)
80 | {
81 | value.ListIntField.Add(random.Next());
82 | }
83 |
84 | return value;
85 | }
86 |
87 | internal static class RandomStringGenerator
88 | {
89 | ///
90 | /// Generate and return a random number
91 | ///
92 | /// random number
93 | public static int RandomNumber()
94 | {
95 | Random random = new Random();
96 | return random.Next(1000, 5000);
97 | }
98 |
99 | ///
100 | /// Generate and return a random string
101 | ///
102 | /// length of the string
103 | /// random string
104 | public static string RandomString(int length)
105 | {
106 | StringBuilder strbuilder = new StringBuilder();
107 | Random random = new Random();
108 | for (int i = 0; i < length; i++)
109 | {
110 | // Generate floating point numbers
111 | double myFloat = random.NextDouble();
112 | // Generate the char
113 | var myChar = Convert.ToChar(Convert.ToInt32(Math.Floor(25 * myFloat) + 65));
114 | strbuilder.Append(myChar);
115 | }
116 | return strbuilder.ToString().ToLower();
117 | }
118 | }
119 | }
120 | }
--------------------------------------------------------------------------------
/Tests/SimpleJSON/SerializeFirstAsObjectTypes.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using TranslationCommon.SimpleJSON;
3 | using UnityEngine;
4 |
5 | namespace Tests
6 | {
7 | public class SerializeFirstAsObjectTypes
8 | {
9 | [SerializeField]
10 | [SerializeFirstAsObject]
11 | private Dictionary _dictionary = new Dictionary();
12 |
13 | public void Add(string key, string value)
14 | {
15 | _dictionary.Add(key, value);
16 | }
17 |
18 | public Dictionary Get()
19 | {
20 | return _dictionary;
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/Tests/SimpleJSON/SimpleJSONTests.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using FluentAssertions;
3 | using Newtonsoft.Json;
4 | using NUnit.Framework;
5 | using TranslationCommon.SimpleJSON;
6 | using TranslationCommon.Translation;
7 | using Assert = NUnit.Framework.Assert;
8 |
9 | namespace Tests
10 | {
11 | public class Tests
12 | {
13 | [Test]
14 | public void Test()
15 | {
16 | var a = new Dictionary();
17 | var t = a.GetType();
18 | }
19 |
20 | [Test]
21 | public void LanguageDataTest()
22 | {
23 | LanguageData data = new LanguageData();
24 | data.TranslationTable = new List();
25 | var list = data.TranslationTable;
26 | for (int i = 0; i < 10; i++)
27 | {
28 | list.Add(new TranslationProto($"Test {i}", i, $"Org {i}", $"Trans {i}"));
29 | }
30 |
31 | var truth = JsonConvert.SerializeObject(data, Formatting.Indented);
32 | NUnit.Framework.Assert.AreEqual(truth, JSON.ToJson(data, true));
33 | var test = JSON.FromJson(truth);
34 | test.Should().BeEquivalentTo(data);
35 | }
36 |
37 | [Test]
38 | public void ComplexDataTypesTest()
39 | {
40 | ComplexDataTypes data = ComplexDataTypes.GenerateRandomValues(null, false);
41 |
42 | var truth = JsonConvert.SerializeObject(data, Formatting.Indented);
43 | NUnit.Framework.Assert.AreEqual(truth, JSON.ToJson(data, true));
44 | var jsonNet = JsonConvert.DeserializeObject(truth);
45 | var test = JSON.FromJson(truth);
46 | jsonNet.Should().BeEquivalentTo(data);
47 | test.Should().BeEquivalentTo(data);
48 | }
49 |
50 | [Test]
51 | public void ComplexDataTypesTestNoIndent()
52 | {
53 | ComplexDataTypes data = ComplexDataTypes.GenerateRandomValues(null, false);
54 |
55 | var truth = JsonConvert.SerializeObject(data, Formatting.None);
56 | NUnit.Framework.Assert.AreEqual(truth, JSON.ToJson(data, false));
57 | var test = JSON.FromJson(truth);
58 | test.Should().BeEquivalentTo(data);
59 | }
60 |
61 | [Test]
62 | public void UnityTypesTest()
63 | {
64 | UnityTypes data = UnityTypes.GenerateRandomValues();
65 |
66 | var result = JSON.ToJson(data, true);
67 | var test = JSON.FromJson(result);
68 | test.Should().BeEquivalentTo(data);
69 | }
70 |
71 | [Test]
72 | public void DictionaryTest()
73 | {
74 | Dictionary data = new Dictionary();
75 | data.Add("Key1", "Value1");
76 | data.Add("Key2", "Value2");
77 |
78 | var truth = JsonConvert.SerializeObject(data, Formatting.Indented);
79 | NUnit.Framework.Assert.AreEqual(truth, JSON.ToJson(data, true));
80 | var jsonNet = JsonConvert.DeserializeObject>(truth);
81 | var test = JSON.FromJson>(truth);
82 | jsonNet.Should().BeEquivalentTo(data);
83 | test.Should().BeEquivalentTo(data);
84 | }
85 |
86 | [Test]
87 | public void SerializeFirstAsObjectTest()
88 | {
89 | var data = new SerializeFirstAsObjectTypes();
90 | data.Add("Test", "test");
91 | data.Add("Test2", "test2");
92 |
93 | var result = JSON.ToJson(data, true);
94 | NUnit.Framework.Assert.AreEqual("{\r\n \"Test\": \"test\",\r\n \"Test2\": \"test2\"\r\n}", result);
95 | var test = JSON.FromJson(result);
96 | test.Get().Should().BeEquivalentTo(data.Get());
97 | }
98 | }
99 | }
--------------------------------------------------------------------------------
/Tests/SimpleJSON/UnityTypes.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using UnityEngine;
3 | using Random = System.Random;
4 |
5 | namespace Tests
6 | {
7 | public class UnityTypes
8 | {
9 | public Vector2 Vector2Field;
10 | public Vector3 Vector3Field;
11 |
12 | public List ListVector2Field;
13 |
14 | public static UnityTypes GenerateRandomValues()
15 | {
16 | UnityTypes value = new UnityTypes();
17 | var random = new Random(0);
18 | value.Vector2Field = new Vector2((float)random.NextDouble(), (float)random.NextDouble());
19 | value.Vector3Field = new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble());
20 |
21 | value.ListVector2Field = new List();
22 | for (int i = 0; i < 3; i++)
23 | {
24 | value.ListVector2Field.Add(new Vector2((float)random.NextDouble(), (float)random.NextDouble()));
25 | }
26 |
27 | return value;
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/Tests/Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net472
5 |
6 | false
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/Tests/UIFixes/FromJsonTests.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using NUnit.Framework;
3 | using TranslationCommon.Fonts;
4 | using TranslationCommon.SimpleJSON;
5 |
6 | namespace Tests.UIFixes
7 | {
8 | [TestFixture]
9 | public class FromJsonTests
10 | {
11 | [Test]
12 | public void TestFromJsonBasic()
13 | {
14 | string json =
15 | "{\"ActionFixMap\":{\"OnInit\":{\"t:UIOptionWindow\":[{\"Path\":\"*details.content*.*\",\"Fix\":{\"anchoredPosition\":\"(+75, +0)\"}}]}}}";
16 | var data = JSON.FromJson(json);
17 | NUnit.Framework.Assert.IsTrue(data.ActionFixMap.Count == 1);
18 | var action = data.ActionFixMap["OnInit"];
19 | Assert.NotNull(action);
20 | var fixes = action.GetByType(new TestableUIBehaviourComponent()
21 | {
22 | Type = "UIOptionWindow"
23 | });
24 | NUnit.Framework.Assert.AreEqual(1, fixes.Count);
25 | NUnit.Framework.Assert.AreEqual("*details.content*.*", fixes[0].Path);
26 | NUnit.Framework.Assert.IsInstanceOf(fixes[0].Fix[0]);
27 | var rectFix = (RectFix)fixes[0].Fix[0];
28 | NUnit.Framework.Assert.AreEqual("(+75, +0)", rectFix.anchoredPosition);
29 | }
30 | }
31 | }
--------------------------------------------------------------------------------
/Tests/UIFixes/RangeValueTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.Linq;
4 | using NUnit.Framework;
5 | using TranslationCommon.Fonts;
6 |
7 | namespace Tests.UIFixes
8 | {
9 | [TestFixture]
10 | public class RangeValueTests
11 | {
12 | [TestCase("-7", -7, 0, 0)]
13 | [TestCase("-7", -7, -10, 1)]
14 | [TestCase("0", 0, 0, 1)]
15 | [TestCase("0", 0, -1, 1)]
16 | [TestCase("1", 1)]
17 | [TestCase("4", 4)]
18 | public void RangeValue_Equal_Test(string test, int expected, int startRange = 0, int expectedCount = 1)
19 | {
20 | var range = RangeValue.Parse(test);
21 | range.Reset(startRange);
22 | var count = 0;
23 | while (range.TryGetNextMatch(out var match))
24 | {
25 | NUnit.Framework.Assert.AreEqual(expected, match);
26 | count++;
27 | }
28 |
29 | NUnit.Framework.Assert.AreEqual(expectedCount, count);
30 | }
31 |
32 | [TestCase("<-7", new []{-10, -9, -8}, -10)]
33 | [TestCase("<0", new int[]{}, 0)]
34 | [TestCase("<4", new []{0, 1, 2, 3})]
35 | public void RangeValue_LessThan_Test(string test, int[] expected, int startRange = 0)
36 | {
37 | RangeValueTestRange(test, expected, startRange);
38 | }
39 |
40 | [TestCase("<=-7", new []{-10, -9, -8, -7}, -10)]
41 | [TestCase("<=0", new int[]{0})]
42 | [TestCase("<=4", new []{0, 1, 2, 3, 4})]
43 | public void RangeValue_LessThanEqual_Test(string test, int[] expected, int startRange = 0)
44 | {
45 | RangeValueTestRange(test, expected, startRange);
46 | }
47 |
48 | [TestCase(">-7", new []{-6, -5, -4,}, -10)]
49 | [TestCase(">0", new int[]{1, 2, 3})]
50 | [TestCase(">4", new []{5, 6, 7})]
51 | public void RangeValue_GreaterThen_Test(string test, int[] expected, int startRange = 0, int iterations = 3)
52 | {
53 | RangeValueTestRange(test, expected, startRange, iterations);
54 | }
55 |
56 | [TestCase(">=-7", new []{-7, -6, -5}, -10)]
57 | [TestCase(">=0", new int[]{0, 1, 2})]
58 | [TestCase(">=4", new []{4, 5, 6})]
59 | public void RangeValue_GreaterThenEqual_Test(string test, int[] expected, int startRange = 0, int iterations = 3)
60 | {
61 | RangeValueTestRange(test, expected, startRange, iterations);
62 | }
63 |
64 | [TestCase("-7..-5", new []{-7, -6, -5}, -10)]
65 | [TestCase("0..0", new int[]{0})]
66 | [TestCase("3..0", new int[]{0, 1, 2, 3})]
67 | [TestCase("0..3", new int[]{0, 1, 2, 3})]
68 | [TestCase("4..6", new []{4, 5, 6})]
69 | public void RangeValue_Between_Test(string test, int[] expected, int startRange = 0)
70 | {
71 | RangeValueTestRange(test, expected, startRange);
72 | }
73 |
74 | [TestCase("-7;-5", new []{-7, -5}, -10)]
75 | [TestCase("0;0", new int[]{0})]
76 | [TestCase("3;0", new int[]{0, 3})]
77 | [TestCase("0;3", new int[]{0, 3})]
78 | [TestCase("4;6", new []{4, 6})]
79 | public void RangeValue_Separator_Test(string test, int[] expected, int startRange = 0)
80 | {
81 | RangeValueTestRange(test, expected, startRange);
82 | }
83 |
84 | [TestCase("3..5; 8; >12", new []{3,4,5,8,13,14}, 6)]
85 | public void RangeValue_Complex_Test(string test, int[] expected, int iterations)
86 | {
87 | RangeValueTestRange(test, expected, 0, iterations);
88 | }
89 |
90 | private static void RangeValueTestRange(string test, int[] expected, int startRange, int iterations = int.MaxValue)
91 | {
92 | var range = RangeValue.Parse(test);
93 | range.Reset(startRange);
94 | var count = 0;
95 | while (range.TryGetNextMatch(out var match) && count < iterations)
96 | {
97 | NUnit.Framework.Assert.AreEqual(true, expected.Contains(match),
98 | $"Expected [{expected.Select(x => x.ToString()).Aggregate((x,y) => $"{x}, {y}")}]\n" +
99 | $"But was [{match}] at Count: {count}");
100 | count++;
101 | }
102 |
103 | NUnit.Framework.Assert.AreEqual(expected.Length, count);
104 | }
105 |
106 | }
107 | }
--------------------------------------------------------------------------------
/Tests/UIFixes/SimpleUIFixesTests.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using NUnit.Framework;
4 | using TranslationCommon.Fonts;
5 | using TranslationCommon.SimpleJSON;
6 |
7 | namespace Tests.UIFixes
8 | {
9 | [TestFixture]
10 | public class SimpleUIFixesTests
11 | {
12 | public UIBehaviourCache Cache = new UIBehaviourCache();
13 | public List Components = new List();
14 |
15 | [SetUp]
16 | public void Setup()
17 | {
18 | Cache = new UIBehaviourCache();
19 | var actionFixesMap = Cache.UIFixes.ActionFixMap;
20 | var actionFixes = new UIActionFixes();
21 | actionFixesMap.Add("OnCreate", actionFixes);
22 | var dictionaryFixes = new Dictionary>();
23 | dictionaryFixes.Add("t:UIAction", new List()
24 | {
25 | new UIFix()
26 | {
27 | Path = "TypeTest"
28 | }
29 | });
30 | dictionaryFixes.Add("p:simplePath", new List()
31 | {
32 | new UIFix()
33 | {
34 | Path = "SimplePath"
35 | }
36 | });
37 | dictionaryFixes.Add("p:*wildCardPath", new List()
38 | {
39 | new UIFix()
40 | {
41 | Path = "StartWildCardPath"
42 | }
43 | });
44 | dictionaryFixes.Add("p:wildCardPath*", new List()
45 | {
46 | new UIFix()
47 | {
48 | Path = "EndWildCardPath"
49 | }
50 | });
51 | dictionaryFixes.Add("p:*multiMatches*", new List()
52 | {
53 | new UIFix()
54 | {
55 | Path = "MultiWildCardPath"
56 | }
57 | });
58 | dictionaryFixes.Add("p:*details.content*.*", new List()
59 | {
60 | new UIFix()
61 | {
62 | Path = "ComplexWildCardPath"
63 | }
64 | });
65 | actionFixes.Initialize(dictionaryFixes);
66 |
67 | var actionFixes2 = new UIActionFixes();
68 | actionFixesMap.Add("OnInit", actionFixes2);
69 | var dictionaryFixes2 = new Dictionary>();
70 | dictionaryFixes2.Add("t:UIAction", new List()
71 | {
72 | new UIFix()
73 | {
74 | Path = "TypeTest"
75 | }
76 | });
77 | actionFixes2.Initialize(dictionaryFixes2);
78 | CreateBehaviourComponents();
79 | }
80 |
81 | private void CreateBehaviourComponents()
82 | {
83 | Components.Add(new TestableUIBehaviourComponent()
84 | {
85 | Path = "Path",
86 | Type = "UIAction",
87 | });
88 | }
89 |
90 | [Test]
91 | public void TestableUIBehaviour_TypeTest()
92 | {
93 | var testable = new TestableUIBehaviourComponent()
94 | {
95 | Path = "Path",
96 | Type = "UIAction",
97 | };
98 | var fixes = Cache.GetFixes("OnCreate", testable);
99 | NUnit.Framework.Assert.AreEqual(1, fixes.Count);
100 | NUnit.Framework.Assert.AreEqual("TypeTest", fixes[0].Path);
101 | }
102 |
103 | [Test]
104 | public void TestableUIBehaviour_PathTest()
105 | {
106 | var testable = new TestableUIBehaviourComponent()
107 | {
108 | Path = "simplePath",
109 | Type = "Type",
110 | };
111 | var fixes = Cache.GetFixes("OnCreate", testable);
112 | NUnit.Framework.Assert.AreEqual(1, fixes.Count);
113 | NUnit.Framework.Assert.AreEqual("SimplePath", fixes[0].Path);
114 | }
115 |
116 | [Test]
117 | public void TestableUIBehaviour_StartWildCardPathTest()
118 | {
119 | var testable = new TestableUIBehaviourComponent()
120 | {
121 | Path = "this.is.some.wildCardPath",
122 | Type = "Type",
123 | };
124 | var fixes = Cache.GetFixes("OnCreate", testable);
125 | NUnit.Framework.Assert.AreEqual(1, fixes.Count);
126 | NUnit.Framework.Assert.AreEqual("StartWildCardPath", fixes[0].Path);
127 | }
128 |
129 | [Test]
130 | public void TestableUIBehaviour_EndWildCardPathTest()
131 | {
132 | var testable = new TestableUIBehaviourComponent()
133 | {
134 | Path = "wildCardPath.start.with.explosion!",
135 | Type = "Type",
136 | };
137 | var fixes = Cache.GetFixes("OnCreate", testable);
138 | NUnit.Framework.Assert.AreEqual(1, fixes.Count);
139 | NUnit.Framework.Assert.AreEqual("EndWildCardPath", fixes[0].Path);
140 | }
141 |
142 | [Test]
143 | public void TestableUIBehaviour_MultiWildCardPathTest()
144 | {
145 | var testable = new TestableUIBehaviourComponent()
146 | {
147 | Path = "wildcards.multiMatches.rocks",
148 | Type = "Type",
149 | };
150 | var fixes = Cache.GetFixes("OnCreate", testable);
151 | NUnit.Framework.Assert.AreEqual(1, fixes.Count);
152 | NUnit.Framework.Assert.AreEqual("MultiWildCardPath", fixes[0].Path);
153 | testable = new TestableUIBehaviourComponent()
154 | {
155 | Path = "multiMatches.rocks",
156 | Type = "Type",
157 | };
158 | fixes = Cache.GetFixes("OnCreate", testable);
159 | NUnit.Framework.Assert.AreEqual(1, fixes.Count);
160 | NUnit.Framework.Assert.AreEqual("MultiWildCardPath", fixes[0].Path);
161 | testable = new TestableUIBehaviourComponent()
162 | {
163 | Path = "wildcards.multiMatches",
164 | Type = "Type",
165 | };
166 | fixes = Cache.GetFixes("OnCreate", testable);
167 | NUnit.Framework.Assert.AreEqual(1, fixes.Count);
168 | NUnit.Framework.Assert.AreEqual("MultiWildCardPath", fixes[0].Path);
169 | }
170 |
171 | [Test]
172 | public void TestableUIBehaviour_MultiMatches()
173 | {
174 | var testable = new TestableUIBehaviourComponent()
175 | {
176 | Path = "simplePath",
177 | Type = "UIAction",
178 | };
179 | var fixes = Cache.GetFixes("OnCreate", testable);
180 | NUnit.Framework.Assert.AreEqual(2, fixes.Count);
181 | NUnit.Framework.Assert.IsTrue(fixes.Select(x => x.Path).Contains("SimplePath"));
182 | NUnit.Framework.Assert.IsTrue(fixes.Select(x => x.Path).Contains("TypeTest"));
183 | }
184 |
185 | [Test]
186 | public void TestableUIBehaviour_ComplexWildCardPathTest()
187 | {
188 | var testable = new TestableUIBehaviourComponent()
189 | {
190 | Path = "Root.Menu.UI.details.content-1.path",
191 | Type = "Type",
192 | };
193 | var fixes = Cache.GetFixes("OnCreate", testable);
194 | NUnit.Framework.Assert.AreEqual(1, fixes.Count);
195 | NUnit.Framework.Assert.AreEqual("ComplexWildCardPath", fixes[0].Path);
196 | }
197 | }
198 | }
--------------------------------------------------------------------------------
/Tests/UIFixes/TestableUIBehaviourComponent.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel;
4 | using TranslationCommon.Fonts;
5 |
6 | namespace Tests.UIFixes
7 | {
8 | public class TestableUIBehaviourComponent : IBehaviourComponent
9 | {
10 | public string Type;
11 | public string Path;
12 |
13 | public string GetComponentType()
14 | {
15 | return Type;
16 | }
17 |
18 | public string GetComponentPath()
19 | {
20 | return Path;
21 | }
22 |
23 | public List GetComponentsByField(string field)
24 | where T : UnityEngine.Component
25 | {
26 | throw new System.NotImplementedException();
27 | }
28 |
29 | public List GetComponentsByPath(string path, List except, RangeValue matchChildrenLayersCount)
30 | where T : UnityEngine.Component
31 | {
32 | throw new System.NotImplementedException();
33 | }
34 |
35 | public List GetComponentsByType(RangeValue matchChildrenLayersCount)
36 | where T : UnityEngine.Component
37 | {
38 | throw new System.NotImplementedException();
39 | }
40 | }
41 | }
--------------------------------------------------------------------------------
/TranslationCommon/ConsoleLogger.cs:
--------------------------------------------------------------------------------
1 | using BepInEx.Logging;
2 |
3 | namespace TranslationCommon
4 | {
5 | ///
6 | /// Console logger to output to BepInEx console
7 | ///
8 | public static class ConsoleLogger
9 | {
10 | public static readonly ManualLogSource LogSource;
11 | static ConsoleLogger()
12 | {
13 | LogSource = Logger.CreateLogSource(nameof(ConsoleLogger));
14 | }
15 |
16 | public static void Log(LogLevel level, object log)
17 | {
18 | LogSource.Log(level, log);
19 | }
20 |
21 | public static void LogDebug(object log)
22 | {
23 | LogSource.LogDebug(log);
24 | }
25 |
26 | public static void LogError(object log)
27 | {
28 | LogSource.LogError(log);
29 | }
30 |
31 | public static void LogFatal(object log)
32 | {
33 | LogSource.LogFatal(log);
34 | }
35 |
36 | public static void LogInfo(object log)
37 | {
38 | LogSource.LogInfo(log);
39 | }
40 |
41 | public static void LogWarning(object log)
42 | {
43 | LogSource.LogWarning(log);
44 | }
45 | }
46 | }
--------------------------------------------------------------------------------
/TranslationCommon/Fonts/CustomFontData.cs:
--------------------------------------------------------------------------------
1 | using Font = UnityEngine.Font;
2 |
3 | namespace DSPTranslationPlugin.UnityHarmony
4 | {
5 | ///
6 | /// Custom font data container
7 | ///
8 | public class CustomFontData
9 | {
10 | ///
11 | /// Is using default font or custom font
12 | ///
13 | public bool IsUsingCustomFont;
14 | ///
15 | /// Custom font data
16 | ///
17 | public Font CustomFont;
18 | }
19 | }
--------------------------------------------------------------------------------
/TranslationCommon/Fonts/Fixes/IBehaviourComponent.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using UnityEngine;
3 |
4 | namespace TranslationCommon.Fonts
5 | {
6 | public interface IBehaviourComponent
7 | {
8 | string GetComponentType();
9 | string GetComponentPath();
10 |
11 | List GetComponentsByField(string field) where T : Component;
12 |
13 | List GetComponentsByPath(string path, List except, RangeValue matchChildrenRange) where T : Component;
14 |
15 | List GetComponentsByType(RangeValue matchChildrenRange) where T : Component;
16 | }
17 | }
--------------------------------------------------------------------------------
/TranslationCommon/Fonts/Fixes/IFix.cs:
--------------------------------------------------------------------------------
1 | namespace TranslationCommon.Fonts
2 | {
3 | public interface IFix
4 | {
5 | void Evaluate(UIFix parent, IBehaviourComponent component);
6 | }
7 | }
--------------------------------------------------------------------------------
/TranslationCommon/Fonts/Fixes/RangeValue.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Globalization;
4 | using UnityEngine;
5 |
6 | namespace TranslationCommon.Fonts
7 | {
8 | ///
9 | /// Range value that can
10 | ///
11 | ///
12 | /// Following strings will provide following range
13 | /// "4" - match only index equal to 4
14 | /// "<4" - match indexes that are larger then 4
15 | /// ">4" - match indexes that are smaller then 4
16 | /// "<=4" - match indexes that are larger or equal 4
17 | /// ">=4" - match indexes that are smaller or equal 4
18 | /// "2..4" - match indexes between 2 inclusive and 4 inclusive
19 | /// "2;4" - match indexes 2 and 4
20 | /// Complex example:
21 | /// "3..5; 8; <12" - match indexes 3 to 5 inclusive, index 8 and all above 12
22 | ///
23 | public class RangeValue
24 | {
25 | ///
26 | /// All tokens used for this range
27 | ///
28 | private readonly List _rangeTokens = new List();
29 | ///
30 | /// Only currently valid tokens for this iteration
31 | ///
32 | private readonly List _validRangeTokens = new List();
33 |
34 | ///
35 | /// Current index
36 | ///
37 | private int _currentIndex = 0;
38 |
39 | ///
40 | /// Parse string to RangeValue
41 | ///
42 | ///
43 | ///
44 | public static RangeValue Parse(string rangeString)
45 | {
46 | return new RangeValue(rangeString);
47 | }
48 |
49 | ///
50 | /// Hide default constructor
51 | ///
52 | [Obsolete]
53 | private RangeValue()
54 | {
55 |
56 | }
57 |
58 | ///
59 | /// Default constructor
60 | ///
61 | ///
62 | private RangeValue(string rangeString)
63 | {
64 | var split = rangeString.Replace(" ", "").Split(';');
65 | foreach (var s in split)
66 | {
67 | var token = Token.GetMatchingToken(s);
68 | _rangeTokens.Add(token);
69 | if (token.TryGetNextValid(_currentIndex, out var nextValid))
70 | {
71 | _validRangeTokens.Add(token);
72 | }
73 | }
74 | }
75 |
76 | ///
77 | /// Try get next match, returns next valid index
78 | ///
79 | ///
80 | ///
81 | public bool TryGetNextMatch(out int match)
82 | {
83 | match = Int32.MaxValue;
84 | for (var i = 0; i < _validRangeTokens.Count; i++)
85 | {
86 | var rangeToken = _validRangeTokens[i];
87 | if (rangeToken.TryGetNextValid(_currentIndex, out var nextValid))
88 | {
89 | match = Mathf.Min(match, nextValid);
90 | }
91 | else
92 | {
93 | _validRangeTokens.Remove(rangeToken);
94 | i--;
95 | }
96 | }
97 |
98 | if (match != Int32.MaxValue)
99 | {
100 | _currentIndex = match + 1;
101 | return true;
102 | }
103 |
104 | match = 0;
105 | return false;
106 | }
107 |
108 | ///
109 | /// Try get next match from provided index, skipping range optimizations
110 | ///
111 | ///
112 | ///
113 | ///
114 | public bool TryGetMatch(int currentIndex, out int match)
115 | {
116 | match = Int32.MaxValue;
117 | for (var i = 0; i < _rangeTokens.Count; i++)
118 | {
119 | var rangeToken = _rangeTokens[i];
120 | if (rangeToken.TryGetNextValid(currentIndex, out var nextValid))
121 | {
122 | match = Mathf.Min(match, nextValid);
123 | }
124 | }
125 |
126 | if (match != Int32.MaxValue)
127 | {
128 | return true;
129 | }
130 |
131 | match = 0;
132 | return false;
133 | }
134 |
135 | ///
136 | /// Resets range iteration
137 | ///
138 | ///
139 | public void Reset(int startRangeValue = 0)
140 | {
141 | _currentIndex = startRangeValue;
142 | _validRangeTokens.Clear();
143 | foreach (var rangeToken in _rangeTokens)
144 | {
145 | if (rangeToken.TryGetNextValid(_currentIndex, out var nextValid))
146 | {
147 | _validRangeTokens.Add(rangeToken);
148 | }
149 | }
150 | }
151 |
152 | ///
153 | /// Base class for token
154 | ///
155 | private abstract class Token
156 | {
157 | ///
158 | /// Create token that matches provided string, string needs to be a single value
159 | ///
160 | ///
161 | ///
162 | ///
163 | public static Token GetMatchingToken(string tokenString)
164 | {
165 | if (Equal.TryParse(tokenString, out var token)) return token;
166 | if (LessThen.TryParse(tokenString, out token)) return token;
167 | if (LessThenEqual.TryParse(tokenString, out token)) return token;
168 | if (GreaterThen.TryParse(tokenString, out token)) return token;
169 | if (GreaterThenEqual.TryParse(tokenString, out token)) return token;
170 | if (Between.TryParse(tokenString, out token)) return token;
171 |
172 | throw new Exception($"Provided string was invalid {tokenString}, please provide valid range token");
173 | }
174 |
175 | ///
176 | /// Try get next value, returns next valid value and true if possible, else false
177 | ///
178 | /// Current range value
179 | /// Next valid output
180 | ///
181 | public abstract bool TryGetNextValid(int current, out int nextValid);
182 | }
183 |
184 | ///
185 | /// Token for Equal operation
186 | ///
187 | private class Equal : Token
188 | {
189 | private int _value;
190 |
191 | public static bool TryParse(string tokenString, out Token token)
192 | {
193 | if (int.TryParse(tokenString, out var result))
194 | {
195 | token = new Equal()
196 | {
197 | _value = result
198 | };
199 | return true;
200 | }
201 | token = null;
202 | return false;
203 | }
204 |
205 | public override bool TryGetNextValid(int current, out int nextValid)
206 | {
207 | if (current <= _value)
208 | {
209 | nextValid = _value;
210 | return true;
211 | }
212 |
213 | nextValid = 0;
214 | return false;
215 | }
216 | }
217 |
218 | ///
219 | /// Token for LessThen operation
220 | ///
221 | private class LessThen : Token
222 | {
223 | private int _value;
224 |
225 | public static bool TryParse(string tokenString, out Token token)
226 | {
227 | if (tokenString[0] == '<' &&
228 | int.TryParse(tokenString.Remove(0, 1), out var result))
229 | {
230 | token = new LessThen()
231 | {
232 | _value = result
233 | };
234 | return true;
235 | }
236 | token = null;
237 | return false;
238 | }
239 |
240 | public override bool TryGetNextValid(int current, out int nextValid)
241 | {
242 | if (current < _value)
243 | {
244 | nextValid = current;
245 | return true;
246 | }
247 |
248 | nextValid = 0;
249 | return false;
250 | }
251 | }
252 |
253 | ///
254 | /// Token for LessThenEqual operation
255 | ///
256 | private class LessThenEqual : Token
257 | {
258 | private int _value;
259 |
260 | public static bool TryParse(string tokenString, out Token token)
261 | {
262 | if (tokenString[0] == '<' && tokenString[1] == '=' &&
263 | int.TryParse(tokenString.Remove(0, 2), out var result))
264 | {
265 | token = new LessThenEqual()
266 | {
267 | _value = result
268 | };
269 | return true;
270 | }
271 | token = null;
272 | return false;
273 | }
274 |
275 | public override bool TryGetNextValid(int current, out int nextValid)
276 | {
277 | if (current <= _value)
278 | {
279 | nextValid = current;
280 | return true;
281 | }
282 |
283 | nextValid = 0;
284 | return false;
285 | }
286 | }
287 |
288 | ///
289 | /// Token for GreaterThen operation
290 | ///
291 | private class GreaterThen : Token
292 | {
293 | private int _value;
294 |
295 | public static bool TryParse(string tokenString, out Token token)
296 | {
297 | if (tokenString[0] == '>' &&
298 | int.TryParse(tokenString.Remove(0, 1), out var result))
299 | {
300 | token = new GreaterThen()
301 | {
302 | _value = result
303 | };
304 | return true;
305 | }
306 | token = null;
307 | return false;
308 | }
309 |
310 | public override bool TryGetNextValid(int current, out int nextValid)
311 | {
312 | if (current > _value)
313 | {
314 | nextValid = current;
315 | return true;
316 | }
317 | else
318 | {
319 | nextValid = _value + 1;
320 | return true;
321 | }
322 | }
323 | }
324 |
325 | ///
326 | /// Token for GreaterThenEqual operation
327 | ///
328 | private class GreaterThenEqual : Token
329 | {
330 | private int _value;
331 |
332 | public static bool TryParse(string tokenString, out Token token)
333 | {
334 | if (tokenString[0] == '>' && tokenString[1] == '=' &&
335 | int.TryParse(tokenString.Remove(0, 2), out var result))
336 | {
337 | token = new GreaterThenEqual()
338 | {
339 | _value = result
340 | };
341 | return true;
342 | }
343 | token = null;
344 | return false;
345 | }
346 |
347 | public override bool TryGetNextValid(int current, out int nextValid)
348 | {
349 | if (current >= _value)
350 | {
351 | nextValid = current;
352 | return true;
353 | }
354 | else
355 | {
356 | nextValid = _value;
357 | return true;
358 | }
359 | }
360 | }
361 |
362 | ///
363 | /// Token for Between operation
364 | ///
365 | private class Between : Token
366 | {
367 | private int _lessThen;
368 | private int _greaterThen;
369 |
370 | public static bool TryParse(string tokenString, out Token token)
371 | {
372 | var split = tokenString.Split(new[] {".."}, StringSplitOptions.RemoveEmptyEntries);
373 |
374 | if (int.TryParse(split[0], out var lessThen) &&
375 | int.TryParse(split[1], out var greaterThen))
376 | {
377 | token = new Between()
378 | {
379 | _lessThen = lessThen < greaterThen ? lessThen : greaterThen,
380 | _greaterThen = lessThen < greaterThen ? greaterThen : lessThen,
381 | };
382 | return true;
383 | }
384 |
385 | token = null;
386 | return false;
387 | }
388 |
389 | public override bool TryGetNextValid(int current, out int nextValid)
390 | {
391 | if (current <= _lessThen)
392 | {
393 | nextValid = _lessThen;
394 | return true;
395 | }
396 | else if (current >= _lessThen && current <= _greaterThen)
397 | {
398 | nextValid = current;
399 | return true;
400 | }
401 |
402 | nextValid = 0;
403 | return false;
404 | }
405 | }
406 | }
407 | }
--------------------------------------------------------------------------------
/TranslationCommon/Fonts/Fixes/RectFix.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using UnityEngine;
3 |
4 | namespace TranslationCommon.Fonts
5 | {
6 | public class RectFix : IFix
7 | {
8 | public string pivot;
9 | public string anchoredPosition;
10 | public string anchorMax;
11 | public string anchorMin;
12 | public string offsetMax;
13 | public string offsetMin;
14 | public string sizeDelta;
15 |
16 | public void Evaluate(UIFix parent, IBehaviourComponent component)
17 | {
18 | var rectTransforms = parent.GetComponents(component);
19 | foreach (var transform in rectTransforms)
20 | {
21 | Fix(transform);
22 | }
23 | }
24 |
25 | private void Fix(RectTransform rTrans)
26 | {
27 | if (!string.IsNullOrEmpty(pivot))
28 | {
29 | rTrans.pivot = rTrans.pivot.SetRelative(pivot);
30 | }
31 | if (!string.IsNullOrEmpty(anchoredPosition))
32 | {
33 | rTrans.anchoredPosition = rTrans.anchoredPosition.SetRelative(anchoredPosition);
34 | }
35 | if (!string.IsNullOrEmpty(anchorMax))
36 | {
37 | rTrans.anchorMax = rTrans.anchorMax.SetRelative(anchorMax);
38 | }
39 | if (!string.IsNullOrEmpty(anchorMin))
40 | {
41 | rTrans.anchorMin = rTrans.anchorMin.SetRelative(anchorMin);
42 | }
43 | if (!string.IsNullOrEmpty(offsetMax))
44 | {
45 | rTrans.offsetMax = rTrans.offsetMax.SetRelative(offsetMax);
46 | }
47 | if (!string.IsNullOrEmpty(offsetMin))
48 | {
49 | rTrans.offsetMin = rTrans.offsetMin.SetRelative(offsetMin);
50 | }
51 | if (!string.IsNullOrEmpty(sizeDelta))
52 | {
53 | rTrans.sizeDelta = rTrans.sizeDelta.SetRelative(sizeDelta);
54 | }
55 | }
56 | }
57 | }
--------------------------------------------------------------------------------
/TranslationCommon/Fonts/Fixes/RelativeValue.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using UnityEngine;
4 |
5 | namespace TranslationCommon.Fonts
6 | {
7 | public static class RelativeValue
8 | {
9 | public static int SetRelative(this int value, string relative)
10 | {
11 | var sign = relative[0];
12 | var relVal = int.Parse(relative.Substring(1));
13 | switch (sign)
14 | {
15 | case '@':
16 | return relVal;
17 | case '+':
18 | return value + relVal;
19 | case '-':
20 | return value - relVal;
21 | case '*':
22 | return value * relVal;
23 | case '/':
24 | return value / relVal;
25 | case '%':
26 | return value % relVal;
27 | case '^':
28 | return (int)Mathf.Pow(value, relVal);
29 | }
30 |
31 | return relVal;
32 | }
33 |
34 | public static float SetRelative(this float value, string relative)
35 | {
36 | var sign = relative[0];
37 | var relVal = float.Parse(relative.Substring(1));
38 | switch (sign)
39 | {
40 | case '@':
41 | return relVal;
42 | case '+':
43 | return value + relVal;
44 | case '-':
45 | return value - relVal;
46 | case '*':
47 | return value * relVal;
48 | case '/':
49 | return value / relVal;
50 | case '%':
51 | return value % relVal;
52 | case '^':
53 | return Mathf.Pow(value, relVal);
54 | }
55 |
56 | return relVal;
57 | }
58 |
59 | public static double SetRelative(this double value, string relative)
60 | {
61 | var sign = relative[0];
62 | var relVal = double.Parse(relative.Substring(1));
63 | switch (sign)
64 | {
65 | case '@':
66 | return relVal;
67 | case '+':
68 | return value + relVal;
69 | case '-':
70 | return value - relVal;
71 | case '*':
72 | return value * relVal;
73 | case '/':
74 | return value / relVal;
75 | case '%':
76 | return value % relVal;
77 | case '^':
78 | return Math.Pow(value, relVal);
79 | }
80 |
81 | return relVal;
82 | }
83 |
84 | public static Vector2 SetRelative(this Vector2 value, string relative)
85 | {
86 | if (relative.StartsWith("(") && relative.EndsWith(")") && relative.Count(c => c == ',') == 1)
87 | {
88 | relative = relative.Substring(1, relative.Length - 2);
89 | relative = relative.Replace(" ", "");
90 | var split = relative.Split(',');
91 | return new Vector2(
92 | value[0].SetRelative(split[0]),
93 | value[1].SetRelative(split[1])
94 | );
95 | }
96 | throw new Exception($"Expected \"(value, value)\" got \"{relative}\"");
97 | }
98 |
99 | public static Vector3 SetRelative(this Vector3 value, string relative)
100 | {
101 | if (relative.StartsWith("(") && relative.EndsWith(")") && relative.Count(c => c == ',') == 2)
102 | {
103 | relative = relative.Substring(1, relative.Length - 2);
104 | relative = relative.Replace(" ", "");
105 | var split = relative.Split(',');
106 | return new Vector3(
107 | value[0].SetRelative(split[0]),
108 | value[1].SetRelative(split[1]),
109 | value[2].SetRelative(split[2])
110 | );
111 | }
112 | throw new Exception($"Expected \"(value, value, value)\" got \"{relative}\"");
113 | }
114 |
115 | public static Rect SetRelative(this Rect value, string relative)
116 | {
117 | if (relative.StartsWith("(") && relative.EndsWith(")") && relative.Count(c => c == ',') == 2)
118 | {
119 | relative = relative.Substring(1, relative.Length - 2);
120 | relative = relative.Replace(" ", "");
121 | var split = relative.Split(',');
122 | return new Rect(
123 | value.x.SetRelative(split[0]),
124 | value.y.SetRelative(split[1]),
125 | value.width.SetRelative(split[2]),
126 | value.height.SetRelative(split[3])
127 | );
128 | }
129 | throw new Exception($"Expected \"(x, y, width, height)\" got \"{relative}\"");
130 | }
131 | }
132 | }
--------------------------------------------------------------------------------
/TranslationCommon/Fonts/Fixes/TextFix.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using UnityEngine;
3 | using UnityEngine.UI;
4 |
5 | namespace TranslationCommon.Fonts
6 | {
7 | public class TextFix : IFix
8 | {
9 | public TextAnchor? alignment;
10 | public string fontSize;
11 | public FontStyle? fontStyle;
12 | public HorizontalWrapMode? horizontalOverflow;
13 | public string lineSpacing;
14 | public VerticalWrapMode? verticalOverflow;
15 | public bool? alignByGeometry;
16 | public bool? resizeTextForBestFit;
17 | public string resizeTextMaxSize;
18 | public string resizeTextMinSize;
19 | public Color? color;
20 |
21 |
22 | public void Evaluate(UIFix parent, IBehaviourComponent component)
23 | {
24 | var texts = parent.GetComponents(component);
25 | foreach (var text in texts)
26 | {
27 | Fix(text);
28 | }
29 | }
30 |
31 | private void Fix(Text text)
32 | {
33 | if (alignment != null)
34 | {
35 | text.alignment = alignment.Value;
36 | }
37 | if (!string.IsNullOrEmpty(fontSize))
38 | {
39 | text.fontSize = text.fontSize.SetRelative(fontSize);
40 | }
41 | if (fontStyle != null)
42 | {
43 | text.fontStyle = fontStyle.Value;
44 | }
45 | if (horizontalOverflow != null)
46 | {
47 | text.horizontalOverflow = horizontalOverflow.Value;
48 | }
49 | if (!string.IsNullOrEmpty(lineSpacing))
50 | {
51 | text.lineSpacing = text.lineSpacing.SetRelative(lineSpacing);
52 | }
53 | if (verticalOverflow != null)
54 | {
55 | text.verticalOverflow = verticalOverflow.Value;
56 | }
57 | if (alignByGeometry != null)
58 | {
59 | text.alignByGeometry = alignByGeometry.Value;
60 | }
61 | if (resizeTextForBestFit != null)
62 | {
63 | text.resizeTextForBestFit = resizeTextForBestFit.Value;
64 | }
65 | if (!string.IsNullOrEmpty(resizeTextMaxSize))
66 | {
67 | text.resizeTextMaxSize = text.resizeTextMaxSize.SetRelative(resizeTextMaxSize);
68 | }
69 | if (!string.IsNullOrEmpty(resizeTextMinSize))
70 | {
71 | text.resizeTextMinSize = text.resizeTextMinSize.SetRelative(resizeTextMinSize);
72 | }
73 | if (color != null)
74 | {
75 | text.color = color.Value;
76 | }
77 | }
78 | }
79 | }
--------------------------------------------------------------------------------
/TranslationCommon/Fonts/Fixes/UIActionFixes.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using TranslationCommon.SimpleJSON;
3 | using UnityEngine;
4 |
5 | namespace TranslationCommon.Fonts
6 | {
7 | public class UIActionFixes
8 | {
9 | [SerializeField]
10 | [SerializeFirstAsObject]
11 | private Dictionary> _serializedFixMap = new Dictionary>();
12 |
13 | private Dictionary> _typeFixMap;
14 | private List _pathFixMap;
15 |
16 | public List GetByType(IBehaviourComponent component)
17 | {
18 | if (_typeFixMap == null)
19 | {
20 | Initialize(_serializedFixMap);
21 | }
22 |
23 | var name = component.GetComponentType();
24 | return _typeFixMap.ContainsKey(name) ? _typeFixMap[name] : new List();
25 | }
26 |
27 | public List GetByPath(IBehaviourComponent component)
28 | {
29 | if (_pathFixMap == null)
30 | {
31 | Initialize(_serializedFixMap);
32 | }
33 |
34 | var name = component.GetComponentPath();
35 | var fixes = new List();
36 | for (int i = 0; i < _pathFixMap.Count; i++)
37 | {
38 | if (name.EqualsWildcard(_pathFixMap[i].Path))
39 | {
40 | fixes.AddRange(_pathFixMap[i].Fixes);
41 | }
42 | }
43 | return fixes;
44 | }
45 |
46 | public void Initialize(Dictionary> serializedFixesMap)
47 | {
48 | _serializedFixMap = serializedFixesMap;
49 | _typeFixMap = new Dictionary>();
50 | _pathFixMap = new List();
51 | foreach (var pair in serializedFixesMap)
52 | {
53 | if (pair.Key.StartsWith("t:"))
54 | {
55 | var key = pair.Key.Remove(0,2);
56 | _typeFixMap.Add(key, pair.Value);
57 | }
58 | else if (pair.Key.StartsWith("p:"))
59 | {
60 | var path = pair.Key.Remove(0,2);
61 | _pathFixMap.Add(new UIPathTarget()
62 | {
63 | Path = path,
64 | Fixes = pair.Value,
65 | });
66 | }
67 | }
68 | }
69 | }
70 | }
--------------------------------------------------------------------------------
/TranslationCommon/Fonts/Fixes/UIBehaviourCache.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using UnityEngine;
3 |
4 | namespace TranslationCommon.Fonts
5 | {
6 | ///
7 | /// Cache of all possible fixes available to be used
8 | ///
9 | public class UIBehaviourCache
10 | {
11 | ///
12 | /// List of fixes - serialized to json
13 | ///
14 | public UIFixesData UIFixes = new UIFixesData();
15 |
16 | ///
17 | /// Get fixes for requested component
18 | ///
19 | ///
20 | ///
21 | ///
22 | public List GetFixes(string actionName, IBehaviourComponent component)
23 | {
24 | var fixes = new List();
25 | var action = UIFixes.Get(actionName);
26 | if (action == null)
27 | {
28 | return null;
29 | }
30 |
31 | fixes.AddRange(action.GetByType(component));
32 | fixes.AddRange(action.GetByPath(component));
33 | if (fixes.Count != 0)
34 | {
35 | ConsoleLogger.LogDebug($"Match! {fixes.Count} {component.GetComponentPath()}");
36 | }
37 | return fixes.Count != 0 ? fixes : null;
38 | }
39 | }
40 | }
--------------------------------------------------------------------------------
/TranslationCommon/Fonts/Fixes/UIBehaviourComponent.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.IO;
3 | using System.Linq;
4 | using System.Reflection;
5 | using TranslationCommon.SimpleJSON;
6 | using UnityEngine;
7 |
8 | namespace TranslationCommon.Fonts
9 | {
10 | /*
11 | * ActionFixMap
12 | * {
13 | * "OnCreate" : {
14 | * "t:UIAction":{...
15 | * }
16 | * "p:*dasdas":{...}
17 | * "p:*dasdas":{...}
18 | * }
19 | * }
20 | *
21 | * {
22 | * Type: "Text",
23 | * Path: "content1",
24 | * Field: "_dspText",
25 | * Text: [],
26 | * RectTransform: [],
27 | * }
28 | */
29 |
30 | public class UIBehaviourComponent : IBehaviourComponent
31 | {
32 | private static UIBehaviourCache _behaviourCache;
33 |
34 | private readonly ManualBehaviour _behaviour;
35 |
36 | public UIBehaviourComponent(ManualBehaviour behaviour)
37 | {
38 | _behaviour = behaviour;
39 | }
40 |
41 | public static UIBehaviourCache BehaviourCache
42 | {
43 | get
44 | {
45 | if (_behaviourCache == null)
46 | {
47 | var path = $"{Utils.ConfigPath}/Translation/fontFix.json";
48 | if (File.Exists(path))
49 | {
50 | _behaviourCache = new UIBehaviourCache();
51 | var json = File.ReadAllText(path);
52 | _behaviourCache.UIFixes = JSON.FromJson(json);
53 | }
54 | }
55 |
56 | return _behaviourCache;
57 | }
58 | }
59 |
60 | public string GetComponentType()
61 | {
62 | return _behaviour.GetType().Name;
63 | }
64 |
65 | public string GetComponentPath()
66 | {
67 | return GetComponentPath(_behaviour);
68 | }
69 |
70 | public List GetComponentsByField(string field) where T : Component
71 | {
72 | var fieldInfos = _behaviour.GetType()
73 | .GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
74 | var limitedFieldInfos = fieldInfos
75 | .Where(info => info.FieldType.IsAssignableFrom(typeof(T)))
76 | .Where(info => info.FieldType.Name.EqualsWildcard(field));
77 | return limitedFieldInfos.Select(info => (T) info.GetValue(_behaviour)).ToList();
78 | }
79 |
80 | public List GetComponentsByPath(string path, List except, RangeValue matchChildrenRange) where T : Component
81 | {
82 | var components = _behaviour.GetComponentsInChildrenWithSelf(matchChildrenRange);
83 | return components
84 | .Where(arg =>
85 | {
86 | var componentPath = GetComponentPath(arg);
87 | if (except != null && except.Any(ex => componentPath.EqualsWildcard(ex)))
88 | {
89 | return false;
90 | }
91 | return componentPath.EqualsWildcard(path);
92 | })
93 | .ToList();
94 | }
95 |
96 | public List GetComponentsByType(RangeValue matchChildrenRange) where T : Component
97 | {
98 | return _behaviour.GetComponentsInChildrenWithSelf(matchChildrenRange).ToList();
99 | }
100 |
101 | public static string GetComponentPath(Component component)
102 | {
103 | var pathList = new List();
104 | var current = component.gameObject.transform;
105 | while (current != null)
106 | {
107 | pathList.Add(current.name);
108 | current = current.parent;
109 | }
110 |
111 | pathList.Reverse();
112 | var join = string.Join(".", pathList.ToArray());
113 | return join;
114 | }
115 |
116 | public void OnCreate()
117 | {
118 | BehaviourCache?.GetFixes("OnCreate", this)?
119 | .ForEach(fix => fix.Fix.ForEach(f => f.Evaluate(fix, this)));
120 | }
121 |
122 | public void OnInit()
123 | {
124 | BehaviourCache?.GetFixes("OnInit", this)?
125 | .ForEach(fix => fix.Fix.ForEach(f => f.Evaluate(fix, this)));
126 | }
127 |
128 | public void OnOpen()
129 | {
130 | BehaviourCache?.GetFixes("OnOpen", this)?
131 | .ForEach(fix => fix.Fix.ForEach(f => f.Evaluate(fix, this)));
132 | }
133 |
134 | public void OnUpdate()
135 | {
136 | BehaviourCache?.GetFixes("OnUpdate", this)?
137 | .ForEach(fix => fix.Fix.ForEach(f => f.Evaluate(fix, this)));
138 | }
139 |
140 | public void OnLateUpdate()
141 | {
142 | BehaviourCache?.GetFixes("OnLateUpdate", this)?
143 | .ForEach(fix => fix.Fix.ForEach(f => f.Evaluate(fix, this)));
144 | }
145 | }
146 | }
--------------------------------------------------------------------------------
/TranslationCommon/Fonts/Fixes/UIFix.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using UnityEngine;
4 |
5 | namespace TranslationCommon.Fonts
6 | {
7 | public class UIFix
8 | {
9 | public string Path;
10 | public string Field;
11 |
12 | public List ExceptPath;
13 |
14 | public string MatchChildrenRange;
15 |
16 | public List Fix;
17 |
18 | public List GetComponents(IBehaviourComponent component)
19 | where T : Component
20 | {
21 | if (!String.IsNullOrEmpty(Path))
22 | {
23 | return component.GetComponentsByPath(Path, ExceptPath, !String.IsNullOrEmpty(MatchChildrenRange) ? RangeValue.Parse(MatchChildrenRange) : null);
24 | }
25 | else if (!String.IsNullOrEmpty(Field))
26 | {
27 | return component.GetComponentsByField(Field);
28 | }
29 | else
30 | {
31 | return component.GetComponentsByType(!String.IsNullOrEmpty(MatchChildrenRange) ? RangeValue.Parse(MatchChildrenRange) : null);
32 | }
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/TranslationCommon/Fonts/Fixes/UIFixesData.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace TranslationCommon.Fonts
4 | {
5 | public class UIFixesData
6 | {
7 | public Dictionary ActionFixMap = new Dictionary();
8 |
9 | ///
10 | /// Get action fixes
11 | ///
12 | ///
13 | ///
14 | public UIActionFixes Get(string actionName)
15 | {
16 | if (ActionFixMap.ContainsKey(actionName))
17 | {
18 | return ActionFixMap[actionName];
19 | }
20 |
21 | return null;
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/TranslationCommon/Fonts/Fixes/UIPathTarget.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace TranslationCommon.Fonts
4 | {
5 | public class UIPathTarget
6 | {
7 | public string Path;
8 | public List Fixes;
9 | }
10 | }
--------------------------------------------------------------------------------
/TranslationCommon/Fonts/TextDefaultFont.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Reflection;
4 | using HarmonyLib;
5 | using UnityEngine;
6 | using UnityEngine.UI;
7 |
8 | namespace DSPTranslationPlugin.UnityHarmony
9 | {
10 | ///
11 | /// Additional container for Text component - this should be always present but it's not a monobehaviour.
12 | /// Use static api of to request underlying font information
13 | ///
14 | public class TextDefaultFont
15 | {
16 | ///
17 | /// Field info helper to access font data
18 | ///
19 | private readonly static FieldInfo FieldInfo_FontData = AccessTools.Field(typeof(Text), "m_FontData");
20 |
21 | ///
22 | /// Default font used by this component
23 | ///
24 | public Font DefaultFont;
25 |
26 | ///
27 | /// Stored text refernece
28 | ///
29 | public Text Reference;
30 |
31 | ///
32 | /// FontData private field reference
33 | ///
34 | private FontData FontData;
35 |
36 | ///
37 | /// Default constructor
38 | ///
39 | ///
40 | public TextDefaultFont(Text reference)
41 | {
42 | Reference = reference;
43 | FontData = (FontData)FieldInfo_FontData.GetValue(Reference);
44 | DefaultFont = FontData.font;
45 | }
46 |
47 | ///
48 | /// Method invoked exclusively by
49 | ///
50 | public void OnGetFont()
51 | {
52 | if (DefaultFont == null || FontData == null) return;
53 |
54 | foreach (var customFont in TextFontManager.CustomFonts)
55 | {
56 | if (customFont.Key == DefaultFont.name ||
57 | (customFont.Key == "Default" && !TextFontManager.CustomFonts.ContainsKey(DefaultFont.name)))
58 | {
59 | if (customFont.Value.IsUsingCustomFont)
60 | {
61 | if (FontData.font != customFont.Value.CustomFont)
62 | {
63 | FontData.font = customFont.Value.CustomFont;
64 | }
65 | }
66 | else
67 | {
68 | if (FontData.font != DefaultFont)
69 | {
70 | FontData.font = DefaultFont;
71 | }
72 | }
73 | }
74 | }
75 | }
76 |
77 | ///
78 | /// Apply custom fond immediately skipping TextFontManager
79 | ///
80 | ///
81 | public void UseCustomFontImmediate(Font fontToUse)
82 | {
83 | Reference.font = fontToUse;
84 | }
85 | }
86 | }
--------------------------------------------------------------------------------
/TranslationCommon/Fonts/TextFontManager.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using TranslationCommon;
5 | using TranslationCommon.Translation;
6 | using UnityEngine;
7 | using UnityEngine.UI;
8 |
9 | namespace DSPTranslationPlugin.UnityHarmony
10 | {
11 | ///
12 | /// Font manager for all text components
13 | ///
14 | public static class TextFontManager
15 | {
16 | ///
17 | /// Original Font name and Custom font pair
18 | ///
19 | public static readonly Dictionary CustomFonts = new Dictionary();
20 |
21 | ///
22 | /// All Text object references with it's corresponding font data
23 | ///
24 | private static readonly Dictionary _textReferences = new Dictionary();
25 |
26 | ///
27 | /// Player prefs key for all saved fonts keys
28 | ///
29 | public const string PlayerPrefsSavedFontsCode = "dps_translate_muchaszewski_fonts";
30 | ///
31 | /// Player prefs key for font used under given name
32 | ///
33 | public const string PlayerPrefsFontCode = "dps_translate_muchaszewski_font_";
34 |
35 | ///
36 | /// List of all installed fonts
37 | ///
38 | public static string[] InstalledFonts { get; private set; }
39 |
40 | ///
41 | /// Static constructor
42 | ///
43 | static TextFontManager()
44 | {
45 | InstalledFonts = Font.GetOSInstalledFontNames();
46 | }
47 |
48 | public static void CheckFonts()
49 | {
50 | List fonts = Font.GetOSInstalledFontNames().ToList();
51 | if (TranslationManager.CurrentLanguage != null && TranslationManager.CurrentLanguage.Fonts != null)
52 | {
53 | foreach (Font font in TranslationManager.CurrentLanguage.Fonts)
54 | {
55 | fonts.Add(font.name);
56 | }
57 | }
58 |
59 | InstalledFonts = fonts.ToArray();
60 | }
61 |
62 | ///
63 | /// Loads text font settings
64 | ///
65 | internal static void LoadSettings()
66 | {
67 | var savedFonts = PlayerPrefs.GetString(PlayerPrefsSavedFontsCode);
68 | var split = savedFonts.Split('|').Where(s => !String.IsNullOrEmpty(s)).ToList();
69 | foreach (var savedFontType in split)
70 | {
71 | var fontName = PlayerPrefs.GetString(PlayerPrefsFontCode + savedFontType);
72 |
73 | if (fontName == savedFontType)
74 | {
75 | RestoreDefaultFont(savedFontType);
76 | }
77 | else
78 | {
79 | Font font;
80 | try
81 | {
82 | if (TranslationManager.CurrentLanguage == null || TranslationManager.CurrentLanguage.Fonts == null)
83 | throw new InvalidOperationException();
84 |
85 | font = TranslationManager.CurrentLanguage.Fonts.First(font1 => font1.name == fontName);
86 | }
87 | catch (InvalidOperationException)
88 | {
89 | font = Font.CreateDynamicFontFromOSFont(fontName, 12);
90 | }
91 |
92 | ApplyCustomFont(savedFontType, font);
93 | }
94 | }
95 | }
96 |
97 | ///
98 | /// Get all currently existing text elements
99 | ///
100 | ///
101 | public static IEnumerable GetExistingTextsElements()
102 | {
103 | return _textReferences.Values;
104 | }
105 |
106 | ///
107 | /// Get text data by it's reference
108 | ///
109 | /// Text reference
110 | ///
111 | public static TextDefaultFont Get(Text text)
112 | {
113 | _textReferences.TryGetValue(text, out var value);
114 | return value;
115 | }
116 |
117 | ///
118 | /// Add text data by it's reference
119 | ///
120 | /// Text reference
121 | public static void Add(Text text)
122 | {
123 | var find = Get(text);
124 | if (find == null)
125 | {
126 | _textReferences.Add(text, new TextDefaultFont(text));
127 | }
128 | }
129 |
130 | ///
131 | /// Remove text data by it's reference
132 | ///
133 | /// Text reference
134 | public static void Remove(Text text)
135 | {
136 | var find = Get(text);
137 | if (find != null)
138 | {
139 | _textReferences.Remove(text);
140 | }
141 | }
142 |
143 | ///
144 | /// Get setting index for menu settings
145 | ///
146 | /// Font key
147 | ///
148 | public static int GetSettingIndex(string key)
149 | {
150 | if (CustomFonts.ContainsKey(key))
151 | {
152 | if (!CustomFonts[key].IsUsingCustomFont) return 0;
153 | var fontName = CustomFonts[key].CustomFont.name;
154 | for (int i = 0; i < InstalledFonts.Length; i++)
155 | {
156 | if(InstalledFonts[i] != fontName) continue;
157 | return i + 1;
158 | }
159 | }
160 |
161 | return 0;
162 | }
163 |
164 | ///
165 | /// Saves settings into player prefs
166 | ///
167 | /// Font key
168 | ///
169 | public static void SaveSettings(string key, string fontName)
170 | {
171 | var savedFonts = PlayerPrefs.GetString(PlayerPrefsSavedFontsCode);
172 | var split = savedFonts.Split('|').Where(s => !String.IsNullOrEmpty(s)).ToList();
173 | if (!split.Contains(key))
174 | {
175 | split.Add(key);
176 | }
177 | PlayerPrefs.SetString(PlayerPrefsFontCode + key, fontName);
178 | PlayerPrefs.SetString(PlayerPrefsSavedFontsCode, String.Join("|", split.ToArray()));
179 | }
180 |
181 | ///
182 | /// Apply provided font to font key group
183 | ///
184 | /// Font key
185 | /// Font to apply
186 | public static void ApplyCustomFont(string key, Font fontToUse)
187 | {
188 | CustomFonts[key] = new CustomFontData()
189 | {
190 | CustomFont = fontToUse,
191 | IsUsingCustomFont = true,
192 | };
193 | SaveSettings(key, fontToUse.name);
194 | foreach (var text in _textReferences)
195 | {
196 | text.Value.UseCustomFontImmediate(fontToUse);
197 | }
198 | }
199 |
200 | ///
201 | /// Restore default font
202 | ///
203 | /// Font key
204 | public static void RestoreDefaultFont(string key)
205 | {
206 | CustomFonts[key] = new CustomFontData()
207 | {
208 | IsUsingCustomFont = false,
209 | };
210 | SaveSettings(key, key);
211 | foreach (var text in _textReferences)
212 | {
213 | text.Value.UseCustomFontImmediate(text.Value.DefaultFont);
214 | }
215 | }
216 | }
217 | }
--------------------------------------------------------------------------------
/TranslationCommon/GameObjectsUtils.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using TranslationCommon.Fonts;
4 | using UnityEngine;
5 |
6 | namespace TranslationCommon
7 | {
8 | public static class GameObjectsUtils
9 | {
10 | public static IEnumerable GetChildrenWithSelf(this Component component, RangeValue childLayers)
11 | {
12 | return GetChildrenWithSelf(component.transform, childLayers);
13 | }
14 |
15 | public static IEnumerable GetChildrenWithSelf(this GameObject gameObject, RangeValue childLayers)
16 | {
17 | return GetChildrenWithSelf(gameObject.transform, childLayers);
18 | }
19 |
20 | public static IEnumerable GetChildrenWithSelf(this Transform transform, RangeValue childLayers)
21 | {
22 | return GetChildrenWithSelf_Impl(transform, childLayers, 0);
23 | }
24 |
25 | private static IEnumerable GetChildrenWithSelf_Impl(this Transform transform,
26 | RangeValue childLayers, int index)
27 | {
28 | if (childLayers == null)
29 | {
30 | yield return transform.gameObject;
31 | yield break;
32 | }
33 |
34 | if (!childLayers.TryGetMatch(index, out var match))
35 | {
36 | yield break;
37 | }
38 |
39 | if (index == match)
40 | {
41 | yield return transform.gameObject;
42 | }
43 |
44 | for (var i = 0; i < transform.childCount; i++)
45 | {
46 | var child = transform.GetChild(i);
47 | foreach (var gameObject in GetChildrenWithSelf_Impl(child, childLayers, index + 1))
48 | {
49 | yield return gameObject;
50 | }
51 | }
52 | }
53 |
54 | public static IEnumerable GetComponentsInChildrenWithSelf(this Component component, RangeValue childLayers)
55 | where T : Component
56 | {
57 | return GetComponentsInChildrenWithSelf(component.transform, childLayers);
58 | }
59 |
60 | public static IEnumerable GetComponentsInChildrenWithSelf(this GameObject gameObject, RangeValue childLayers)
61 | where T : Component
62 | {
63 | return GetComponentsInChildrenWithSelf(gameObject.transform, childLayers);
64 | }
65 |
66 | public static IEnumerable GetComponentsInChildrenWithSelf(this Transform transform, RangeValue childLayers)
67 | where T : Component
68 | {
69 | return GetChildrenWithSelf(transform, childLayers).SelectMany(gameObject => gameObject.GetComponents());
70 | }
71 | }
72 | }
--------------------------------------------------------------------------------
/TranslationCommon/SimpleJSON/Attributes.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace TranslationCommon.SimpleJSON
4 | {
5 | public class SerializeFirstAsObjectAttribute : Attribute
6 | {
7 |
8 | }
9 | }
--------------------------------------------------------------------------------
/TranslationCommon/SimpleJSON/Changelog.txt:
--------------------------------------------------------------------------------
1 | /*
2 | * [2012-06-09 First Version]
3 | * - provides strongly typed node classes and lists / dictionaries
4 | * - provides easy access to class members / array items / data values
5 | * - the parser now properly identifies types. So generating JSON with this framework should work.
6 | * - only double quotes (") are used for quoting strings.
7 | * - provides "casting" properties to easily convert to / from those types:
8 | * int / float / double / bool
9 | * - provides a common interface for each node so no explicit casting is required.
10 | * - the parser tries to avoid errors, but if malformed JSON is parsed the result is more or less undefined
11 | * - It can serialize/deserialize a node tree into/from an experimental compact binary format. It might
12 | * be handy if you want to store things in a file and don't want it to be easily modifiable
13 | *
14 | * [2012-12-17 Update]
15 | * - Added internal JSONLazyCreator class which simplifies the construction of a JSON tree
16 | * Now you can simple reference any item that doesn't exist yet and it will return a JSONLazyCreator
17 | * The class determines the required type by it's further use, creates the type and removes itself.
18 | * - Added binary serialization / deserialization.
19 | * - Added support for BZip2 zipped binary format. Requires the SharpZipLib ( http://www.icsharpcode.net/opensource/sharpziplib/ )
20 | * The usage of the SharpZipLib library can be disabled by removing or commenting out the USE_SharpZipLib define at the top
21 | * - The serializer uses different types when it comes to store the values. Since my data values
22 | * are all of type string, the serializer will "try" which format fits best. The order is: int, float, double, bool, string.
23 | * It's not the most efficient way but for a moderate amount of data it should work on all platforms.
24 | *
25 | * [2017-03-08 Update]
26 | * - Optimised parsing by using a StringBuilder for token. This prevents performance issues when large
27 | * string data fields are contained in the json data.
28 | * - Finally refactored the badly named JSONClass into JSONObject.
29 | * - Replaced the old JSONData class by distict typed classes ( JSONString, JSONNumber, JSONBool, JSONNull ) this
30 | * allows to propertly convert the node tree back to json without type information loss. The actual value
31 | * parsing now happens at parsing time and not when you actually access one of the casting properties.
32 | *
33 | * [2017-04-11 Update]
34 | * - Fixed parsing bug where empty string values have been ignored.
35 | * - Optimised "ToString" by using a StringBuilder internally. This should heavily improve performance for large files
36 | * - Changed the overload of "ToString(string aIndent)" to "ToString(int aIndent)"
37 | *
38 | * [2017-11-29 Update]
39 | * - Removed the IEnumerator implementations on JSONArray & JSONObject and replaced it with a common
40 | * struct Enumerator in JSONNode that should avoid garbage generation. The enumerator always works
41 | * on KeyValuePair, even for JSONArray.
42 | * - Added two wrapper Enumerators that allows for easy key or value enumeration. A JSONNode now has
43 | * a "Keys" and a "Values" enumerable property. Those are also struct enumerators / enumerables
44 | * - A KeyValuePair can now be implicitly converted into a JSONNode. This allows
45 | * a foreach loop over a JSONNode to directly access the values only. Since KeyValuePair as well as
46 | * all the Enumerators are structs, no garbage is allocated.
47 | * - To add Linq support another "LinqEnumerator" is available through the "Linq" property. This
48 | * enumerator does implement the generic IEnumerable interface so most Linq extensions can be used
49 | * on this enumerable object. This one does allocate memory as it's a wrapper class.
50 | * - The Escape method now escapes all control characters (# < 32) in strings as uncode characters
51 | * (\uXXXX) and if the static bool JSONNode.forceASCII is set to true it will also escape all
52 | * characters # > 127. This might be useful if you require an ASCII output. Though keep in mind
53 | * when your strings contain many non-ascii characters the strings become much longer (x6) and are
54 | * no longer human readable.
55 | * - The node types JSONObject and JSONArray now have an "Inline" boolean switch which will default to
56 | * false. It can be used to serialize this element inline even you serialize with an indented format
57 | * This is useful for arrays containing numbers so it doesn't place every number on a new line
58 | * - Extracted the binary serialization code into a seperate extension file. All classes are now declared
59 | * as "partial" so an extension file can even add a new virtual or abstract method / interface to
60 | * JSONNode and override it in the concrete type classes. It's of course a hacky approach which is
61 | * generally not recommended, but i wanted to keep everything tightly packed.
62 | * - Added a static CreateOrGet method to the JSONNull class. Since this class is immutable it could
63 | * be reused without major problems. If you have a lot null fields in your data it will help reduce
64 | * the memory / garbage overhead. I also added a static setting (reuseSameInstance) to JSONNull
65 | * (default is true) which will change the behaviour of "CreateOrGet". If you set this to false
66 | * CreateOrGet will not reuse the cached instance but instead create a new JSONNull instance each time.
67 | * I made the JSONNull constructor private so if you need to create an instance manually use
68 | * JSONNull.CreateOrGet()
69 | *
70 | * [2018-01-09 Update]
71 | * - Changed all double.TryParse and double.ToString uses to use the invariant culture to avoid problems
72 | * on systems with a culture that uses a comma as decimal point.
73 | *
74 | * [2018-01-26 Update]
75 | * - Added AsLong. Note that a JSONNumber is stored as double and can't represent all long values. However
76 | * storing it as string would work.
77 | * - Added static setting "JSONNode.longAsString" which controls the default type that is used by the
78 | * LazyCreator when using AsLong
79 | *
80 | * [2018-04-25 Update]
81 | * - Added support for parsing single values (JSONBool, JSONString, JSONNumber, JSONNull) as top level value.
82 | *
83 | * [2019-02-18 Update]
84 | * - Added HasKey(key) and GetValueOrDefault(key, default) to the JSONNode class to provide way to read
85 | * values conditionally without creating a LazyCreator
86 | *
87 | * [2019-03-25 Update]
88 | * - Added static setting "allowLineComments" to the JSONNode class which is true by default. This allows
89 | * "//" line comments when parsing json text as long as it's not within quoted text. All text after // up
90 | * to the end of the line is completely ignored / skipped. This makes it easier to create human readable
91 | * and editable files. Note that stripped comments are not read, processed or preserved in any way. So
92 | * this feature is only relevant for human created files.
93 | * - Explicitly strip BOM (Byte Order Mark) when parsing to avoid getting it leaked into a single primitive
94 | * value. That's a rare case but better safe than sorry.
95 | * - Allowing adding the empty string as key
96 | *
97 | * [2019-12-10 Update]
98 | * - Added Clone() method to JSONNode to allow cloning of a whole node tree.
99 | *
100 | * [2020-09-19 Update]
101 | * - Added Clear() method to JSONNode.
102 | * - The parser will now automatically mark arrays or objects as inline when it doesn't contain any
103 | * new line characters. This should more or less preserve the layout.
104 | * - Added new extension file "SimpleJSONDotNetTypes.cs" to provide support for some basic .NET types
105 | * like decimal, char, byte, sbyte, short, ushort, uint, DateTime, TimeSpan and Guid as well as some
106 | * nullable types.
107 | * - Fixed an error in the Unity extension file. The Color component order was wrong (it was argb, now it's rgba)
108 | * - There are now two static float variables (ColorDefaultAlpha and Color32DefaultAlpha) to specify the default
109 | * alpha values when reading UnityEngine.Color / Color32 values where the alpha value is absent. The default
110 | * values are 1.0f and 255 respectively.
111 | */
112 |
--------------------------------------------------------------------------------
/TranslationCommon/SimpleJSON/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2012-2017 Markus Göbel (Bunny83)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/TranslationCommon/SimpleJSON/README:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muchaszewski/DSP_TranslationMod/bc74e3ae3500ce3c4fb86b7191a9cc5b9e56ee2f/TranslationCommon/SimpleJSON/README
--------------------------------------------------------------------------------
/TranslationCommon/SimpleJSON/ReflectionUtils.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Reflection;
4 |
5 | namespace TranslationCommon.SimpleJSON
6 | {
7 | public static class ReflectionUtils
8 | {
9 | public static bool ContainsAttribute(this MemberInfo memberInfo)
10 | where T : Attribute
11 | {
12 | var customAttributes = memberInfo.GetCustomAttributes(true);
13 | return customAttributes.FirstOrDefault(o => o.GetType().IsAssignableFrom(typeof(T))) != null;
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/TranslationCommon/SimpleJSON/SimpleJSONBinary.cs:
--------------------------------------------------------------------------------
1 | //#define USE_SharpZipLib
2 | /* * * * *
3 | * This is an extension of the SimpleJSON framework to provide methods to
4 | * serialize a JSON object tree into a compact binary format. Optionally the
5 | * binary stream can be compressed with the SharpZipLib when using the define
6 | * "USE_SharpZipLib"
7 | *
8 | * Those methods where originally part of the framework but since it's rarely
9 | * used I've extracted this part into this seperate module file.
10 | *
11 | * You can use the define "SimpleJSON_ExcludeBinary" to selectively disable
12 | * this extension without the need to remove the file from the project.
13 | *
14 | * If you want to use compression when saving to file / stream / B64 you have to include
15 | * SharpZipLib ( http://www.icsharpcode.net/opensource/sharpziplib/ ) in your project and
16 | * define "USE_SharpZipLib" at the top of the file
17 | *
18 | *
19 | * The MIT License (MIT)
20 | *
21 | * Copyright (c) 2012-2017 Markus Göbel (Bunny83)
22 | *
23 | * Permission is hereby granted, free of charge, to any person obtaining a copy
24 | * of this software and associated documentation files (the "Software"), to deal
25 | * in the Software without restriction, including without limitation the rights
26 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
27 | * copies of the Software, and to permit persons to whom the Software is
28 | * furnished to do so, subject to the following conditions:
29 | *
30 | * The above copyright notice and this permission notice shall be included in all
31 | * copies or substantial portions of the Software.
32 | *
33 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
39 | * SOFTWARE.
40 | *
41 | * * * * */
42 |
43 | using System;
44 |
45 | namespace TranslationCommon.SimpleJSON
46 | {
47 | #if !SimpleJSON_ExcludeBinary
48 | public abstract partial class JSONNode
49 | {
50 | public abstract void SerializeBinary(System.IO.BinaryWriter aWriter);
51 |
52 | public void SaveToBinaryStream(System.IO.Stream aData)
53 | {
54 | var W = new System.IO.BinaryWriter(aData);
55 | SerializeBinary(W);
56 | }
57 |
58 | #if USE_SharpZipLib
59 | public void SaveToCompressedStream(System.IO.Stream aData)
60 | {
61 | using (var gzipOut = new ICSharpCode.SharpZipLib.BZip2.BZip2OutputStream(aData))
62 | {
63 | gzipOut.IsStreamOwner = false;
64 | SaveToBinaryStream(gzipOut);
65 | gzipOut.Close();
66 | }
67 | }
68 |
69 | public void SaveToCompressedFile(string aFileName)
70 | {
71 |
72 | System.IO.Directory.CreateDirectory((new System.IO.FileInfo(aFileName)).Directory.FullName);
73 | using(var F = System.IO.File.OpenWrite(aFileName))
74 | {
75 | SaveToCompressedStream(F);
76 | }
77 | }
78 | public string SaveToCompressedBase64()
79 | {
80 | using (var stream = new System.IO.MemoryStream())
81 | {
82 | SaveToCompressedStream(stream);
83 | stream.Position = 0;
84 | return System.Convert.ToBase64String(stream.ToArray());
85 | }
86 | }
87 |
88 | #else
89 | public void SaveToCompressedStream(System.IO.Stream aData)
90 | {
91 | throw new Exception("Can't use compressed functions. You need include the SharpZipLib and uncomment the define at the top of SimpleJSON");
92 | }
93 |
94 | public void SaveToCompressedFile(string aFileName)
95 | {
96 | throw new Exception("Can't use compressed functions. You need include the SharpZipLib and uncomment the define at the top of SimpleJSON");
97 | }
98 |
99 | public string SaveToCompressedBase64()
100 | {
101 | throw new Exception("Can't use compressed functions. You need include the SharpZipLib and uncomment the define at the top of SimpleJSON");
102 | }
103 | #endif
104 |
105 | public void SaveToBinaryFile(string aFileName)
106 | {
107 | System.IO.Directory.CreateDirectory((new System.IO.FileInfo(aFileName)).Directory.FullName);
108 | using (var F = System.IO.File.OpenWrite(aFileName))
109 | {
110 | SaveToBinaryStream(F);
111 | }
112 | }
113 |
114 | public string SaveToBinaryBase64()
115 | {
116 | using (var stream = new System.IO.MemoryStream())
117 | {
118 | SaveToBinaryStream(stream);
119 | stream.Position = 0;
120 | return System.Convert.ToBase64String(stream.ToArray());
121 | }
122 | }
123 |
124 | public static JSONNode DeserializeBinary(System.IO.BinaryReader aReader)
125 | {
126 | JSONNodeType type = (JSONNodeType)aReader.ReadByte();
127 | switch (type)
128 | {
129 | case JSONNodeType.Array:
130 | {
131 | int count = aReader.ReadInt32();
132 | JSONArray tmp = new JSONArray();
133 | for (int i = 0; i < count; i++)
134 | tmp.Add(DeserializeBinary(aReader));
135 | return tmp;
136 | }
137 | case JSONNodeType.Object:
138 | {
139 | int count = aReader.ReadInt32();
140 | JSONObject tmp = new JSONObject();
141 | for (int i = 0; i < count; i++)
142 | {
143 | string key = aReader.ReadString();
144 | var val = DeserializeBinary(aReader);
145 | tmp.Add(key, val);
146 | }
147 | return tmp;
148 | }
149 | case JSONNodeType.String:
150 | {
151 | return new JSONString(aReader.ReadString());
152 | }
153 | case JSONNodeType.Number:
154 | {
155 | return new JSONNumber(aReader.ReadDouble());
156 | }
157 | case JSONNodeType.Boolean:
158 | {
159 | return new JSONBool(aReader.ReadBoolean());
160 | }
161 | case JSONNodeType.NullValue:
162 | {
163 | return JSONNull.CreateOrGet();
164 | }
165 | default:
166 | {
167 | throw new Exception("Error deserializing JSON. Unknown tag: " + type);
168 | }
169 | }
170 | }
171 |
172 | #if USE_SharpZipLib
173 | public static JSONNode LoadFromCompressedStream(System.IO.Stream aData)
174 | {
175 | var zin = new ICSharpCode.SharpZipLib.BZip2.BZip2InputStream(aData);
176 | return LoadFromBinaryStream(zin);
177 | }
178 | public static JSONNode LoadFromCompressedFile(string aFileName)
179 | {
180 | using(var F = System.IO.File.OpenRead(aFileName))
181 | {
182 | return LoadFromCompressedStream(F);
183 | }
184 | }
185 | public static JSONNode LoadFromCompressedBase64(string aBase64)
186 | {
187 | var tmp = System.Convert.FromBase64String(aBase64);
188 | var stream = new System.IO.MemoryStream(tmp);
189 | stream.Position = 0;
190 | return LoadFromCompressedStream(stream);
191 | }
192 | #else
193 | public static JSONNode LoadFromCompressedFile(string aFileName)
194 | {
195 | throw new Exception("Can't use compressed functions. You need include the SharpZipLib and uncomment the define at the top of SimpleJSON");
196 | }
197 |
198 | public static JSONNode LoadFromCompressedStream(System.IO.Stream aData)
199 | {
200 | throw new Exception("Can't use compressed functions. You need include the SharpZipLib and uncomment the define at the top of SimpleJSON");
201 | }
202 |
203 | public static JSONNode LoadFromCompressedBase64(string aBase64)
204 | {
205 | throw new Exception("Can't use compressed functions. You need include the SharpZipLib and uncomment the define at the top of SimpleJSON");
206 | }
207 | #endif
208 |
209 | public static JSONNode LoadFromBinaryStream(System.IO.Stream aData)
210 | {
211 | using (var R = new System.IO.BinaryReader(aData))
212 | {
213 | return DeserializeBinary(R);
214 | }
215 | }
216 |
217 | public static JSONNode LoadFromBinaryFile(string aFileName)
218 | {
219 | using (var F = System.IO.File.OpenRead(aFileName))
220 | {
221 | return LoadFromBinaryStream(F);
222 | }
223 | }
224 |
225 | public static JSONNode LoadFromBinaryBase64(string aBase64)
226 | {
227 | var tmp = System.Convert.FromBase64String(aBase64);
228 | var stream = new System.IO.MemoryStream(tmp);
229 | stream.Position = 0;
230 | return LoadFromBinaryStream(stream);
231 | }
232 | }
233 |
234 | public partial class JSONArray : JSONNode
235 | {
236 | public override void SerializeBinary(System.IO.BinaryWriter aWriter)
237 | {
238 | aWriter.Write((byte)JSONNodeType.Array);
239 | aWriter.Write(m_List.Count);
240 | for (int i = 0; i < m_List.Count; i++)
241 | {
242 | m_List[i].SerializeBinary(aWriter);
243 | }
244 | }
245 | }
246 |
247 | public partial class JSONObject : JSONNode
248 | {
249 | public override void SerializeBinary(System.IO.BinaryWriter aWriter)
250 | {
251 | aWriter.Write((byte)JSONNodeType.Object);
252 | aWriter.Write(m_Dict.Count);
253 | foreach (string K in m_Dict.Keys)
254 | {
255 | aWriter.Write(K);
256 | m_Dict[K].SerializeBinary(aWriter);
257 | }
258 | }
259 | }
260 |
261 | public partial class JSONString : JSONNode
262 | {
263 | public override void SerializeBinary(System.IO.BinaryWriter aWriter)
264 | {
265 | aWriter.Write((byte)JSONNodeType.String);
266 | aWriter.Write(m_Data);
267 | }
268 | }
269 |
270 | public partial class JSONNumber : JSONNode
271 | {
272 | public override void SerializeBinary(System.IO.BinaryWriter aWriter)
273 | {
274 | aWriter.Write((byte)JSONNodeType.Number);
275 | aWriter.Write(m_Data);
276 | }
277 | }
278 |
279 | public partial class JSONBool : JSONNode
280 | {
281 | public override void SerializeBinary(System.IO.BinaryWriter aWriter)
282 | {
283 | aWriter.Write((byte)JSONNodeType.Boolean);
284 | aWriter.Write(m_Data);
285 | }
286 | }
287 | public partial class JSONNull : JSONNode
288 | {
289 | public override void SerializeBinary(System.IO.BinaryWriter aWriter)
290 | {
291 | aWriter.Write((byte)JSONNodeType.NullValue);
292 | }
293 | }
294 | internal partial class JSONLazyCreator : JSONNode
295 | {
296 | public override void SerializeBinary(System.IO.BinaryWriter aWriter)
297 | {
298 |
299 | }
300 | }
301 | #endif
302 | }
303 |
--------------------------------------------------------------------------------
/TranslationCommon/SimpleJSON/SimpleJSONBuilder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.Globalization;
5 | using System.Linq;
6 | using System.Reflection;
7 | using System.Text;
8 | using UnityEngine;
9 |
10 | namespace TranslationCommon.SimpleJSON
11 | {
12 | public class SimpleJSONBuilder
13 | {
14 | public T Deserialize(string serializationStream)
15 | where T : new()
16 | {
17 | var jsonNode = JSON.Parse(serializationStream);
18 | var settings = new SimpleJSONParserSettings();
19 | return (T) SimpleJSONStaticParser.FromJsonNode(jsonNode, typeof(T), settings);
20 | }
21 |
22 | public string Serialize(object graph, bool isIndented)
23 | {
24 | var settings = new SimpleJSONParserSettings
25 | {
26 | IsIndented = isIndented
27 | };
28 | var jsonNode = SimpleJSONStaticParser.ToJsonNode(graph, settings);
29 |
30 | var sb = new StringBuilder();
31 | jsonNode.WriteToStringBuilder(sb, 0, settings.IsIndented ? 2 : 0,
32 | settings.IsIndented ? JSONTextMode.Indent : JSONTextMode.Compact);
33 | return sb.ToString();
34 | }
35 | }
36 |
37 | public struct SimpleJSONParserSettings
38 | {
39 | public static SimpleJSONParserSettings Default()
40 | {
41 | return new SimpleJSONParserSettings
42 | {
43 | IsIndented = true
44 | };
45 | }
46 |
47 | public bool IsIndented;
48 | }
49 |
50 | public static class SimpleJSONStaticParser
51 | {
52 | public static Type[] AppDomainTypes;
53 |
54 | public static object FromJsonNode(JSONNode value, Type type, SimpleJSONParserSettings settings)
55 | {
56 | if (value == null)
57 | {
58 | return null;
59 | }
60 |
61 | if (type == typeof(string))
62 | {
63 | return (string) value;
64 | }
65 |
66 | if (type == typeof(char))
67 | {
68 | return (char) value;
69 | }
70 |
71 | if (type == typeof(bool))
72 | {
73 | return (bool) value;
74 | }
75 |
76 | if (typeof(IList).IsAssignableFrom(type))
77 | {
78 | var listType = typeof(List<>);
79 | var elementType = type.GetGenericArguments();
80 | var constructedListType = listType.MakeGenericType(elementType);
81 | var list = (IList) Activator.CreateInstance(constructedListType);
82 | foreach (var array in value.AsArray.Children)
83 | {
84 | list.Add(FromJsonNode(array, elementType[0], settings));
85 | }
86 |
87 | return list;
88 | }
89 |
90 | if (typeof(IDictionary).IsAssignableFrom(type))
91 | {
92 | var dictType = typeof(Dictionary<,>);
93 | var elementType = type.GetGenericArguments();
94 | var constructedListType = dictType.MakeGenericType(elementType);
95 | var dictionary = (IDictionary) Activator.CreateInstance(constructedListType);
96 | foreach (var dict in value)
97 | {
98 | dictionary.Add(
99 | FromJsonNode(dict.Key, elementType[0], settings),
100 | FromJsonNode(dict.Value, elementType[1], settings)
101 | );
102 | }
103 |
104 | return dictionary;
105 | }
106 |
107 | if (type == typeof(Vector2))
108 | {
109 | return (Vector2) value;
110 | }
111 |
112 | if (type == typeof(Vector3))
113 | {
114 | return (Vector3) value;
115 | }
116 |
117 | if (type == typeof(Vector4))
118 | {
119 | return (Vector4) value;
120 | }
121 |
122 | if (type == typeof(Quaternion))
123 | {
124 | return (Quaternion) value;
125 | }
126 |
127 | if (type == typeof(Rect))
128 | {
129 | return (Rect) value;
130 | }
131 |
132 | if (type == typeof(RectOffset))
133 | {
134 | return (RectOffset) value;
135 | }
136 |
137 | if (type == typeof(Matrix4x4))
138 | {
139 | return (Matrix4x4) value;
140 | }
141 |
142 | if (type == typeof(Color))
143 | {
144 | return (Color) value;
145 | }
146 |
147 | if (type == typeof(Color32))
148 | {
149 | return (Color32) value;
150 | }
151 |
152 | if (type == typeof(byte))
153 | {
154 | return (byte) value;
155 | }
156 |
157 | if (type == typeof(sbyte))
158 | {
159 | return (sbyte) value;
160 | }
161 |
162 | if (type == typeof(int))
163 | {
164 | return (int) value;
165 | }
166 |
167 | if (type == typeof(uint))
168 | {
169 | return (uint) value;
170 | }
171 |
172 | if (type == typeof(short))
173 | {
174 | return (short) value;
175 | }
176 |
177 | if (type == typeof(ushort))
178 | {
179 | return (ushort) value;
180 | }
181 |
182 | if (type == typeof(char))
183 | {
184 | return (char) value;
185 | }
186 |
187 | if (type == typeof(float))
188 | {
189 | return (float) value;
190 | }
191 |
192 | if (type == typeof(double))
193 | {
194 | return (double) value;
195 | }
196 |
197 | if (type == typeof(decimal))
198 | {
199 | return (decimal) value;
200 | }
201 |
202 | if (type == typeof(long))
203 | {
204 | return (long) value;
205 | }
206 |
207 | if (type == typeof(ulong))
208 | {
209 | return (ulong) value;
210 | }
211 |
212 | var obj = CreateMatchingType(value, type, out var resultType);
213 | var fields = resultType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
214 | foreach (var fieldInfo in fields)
215 | {
216 | if (fieldInfo.IsPublic || fieldInfo.ContainsAttribute())
217 | {
218 | if (fieldInfo.ContainsAttribute())
219 | {
220 | fieldInfo.SetValue(obj, FromJsonNode(value, fieldInfo.FieldType, settings));
221 | return obj;
222 | }
223 |
224 | fieldInfo.SetValue(obj, FromJsonNode(value[fieldInfo.Name], fieldInfo.FieldType, settings));
225 | }
226 | }
227 |
228 | return obj;
229 | }
230 |
231 | private static object CreateMatchingType(JSONNode value, Type type, out Type resultType)
232 | {
233 | if (type.IsInterface)
234 | {
235 | bool ContainsAllItems(IEnumerable a, IEnumerable b)
236 | {
237 | return !b.Except(a).Any();
238 | }
239 |
240 | if (AppDomainTypes == null)
241 | {
242 | var assemblies = AppDomain.CurrentDomain.GetAssemblies();
243 | var matchingTypes = new List();
244 | foreach (var assembly in assemblies)
245 | {
246 | try
247 | {
248 | matchingTypes.AddRange(assembly.GetTypes()
249 | .Where(type.IsAssignableFrom)
250 | .Where(t => !t.IsInterface && !t.IsAbstract));
251 | }
252 | catch //(ReflectionTypeLoadException e)
253 | {
254 | /*ConsoleLogger.LogDebug(assembly.FullName + " " + e.Message + "\n" + e.StackTrace + "\n" +
255 | e.LoaderExceptions.Select(exception => exception.Message).Aggregate((x,y) => x + " " + y));*/
256 | }
257 | }
258 | AppDomainTypes = matchingTypes.ToArray();
259 | }
260 |
261 | foreach (var matchingType in AppDomainTypes)
262 | {
263 | var fields =
264 | matchingType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
265 | if (ContainsAllItems(fields.Select(info => info.Name), value.GetEnumerator().ToKeyEnumerable()))
266 | {
267 | resultType = matchingType;
268 | return Activator.CreateInstance(matchingType);
269 | }
270 | }
271 |
272 | throw new KeyNotFoundException(
273 | $"No matching member was not found with signature {value.GetEnumerator().ToKeyEnumerable().Aggregate((x, y) => x + " " + y)}");
274 | }
275 |
276 | resultType = type;
277 | return Activator.CreateInstance(type);
278 | }
279 |
280 | public static JSONNode ToJsonNode(object value, SimpleJSONParserSettings settings)
281 | {
282 | switch (value)
283 | {
284 | case null:
285 | return JSONNull.CreateOrGet();
286 | case JSONNode jsonValue:
287 | return jsonValue;
288 | case string strValue:
289 | return new JSONString(strValue);
290 | case char charValue:
291 | return new JSONString(new string(charValue, 1));
292 | case bool boolValue:
293 | return new JSONBool(boolValue);
294 | case IList listValue:
295 | return ToJsonNode(listValue, settings);
296 | case IDictionary dictValue:
297 | return ToJsonNode(dictValue, settings);
298 |
299 | case Vector2 v2Value:
300 | return v2Value;
301 | case Vector3 v3Value:
302 | return v3Value;
303 | case Vector4 v4Value:
304 | return v4Value;
305 | case Quaternion quatValue:
306 | return quatValue;
307 | case Rect rectValue:
308 | return rectValue;
309 | case RectOffset rectOffsetValue:
310 | return rectOffsetValue;
311 | case Matrix4x4 matrixValue:
312 | return matrixValue;
313 | case Color colorValue:
314 | return colorValue;
315 | case Color32 color32Value:
316 | return color32Value;
317 |
318 | case float floatValue:
319 | return new JSONNumber(floatValue.ToString("R", CultureInfo.InvariantCulture));
320 | default:
321 | if (JSONNumber.IsNumeric(value))
322 | {
323 | return new JSONNumber(Convert.ToDouble(value));
324 | }
325 | else
326 | {
327 | var jsonObject = new JSONObject();
328 | var fields = value.GetType()
329 | .GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
330 | foreach (var fieldInfo in fields)
331 | {
332 | if (fieldInfo.IsPublic || fieldInfo.ContainsAttribute())
333 | {
334 | var fieldValue = fieldInfo.GetValue(value);
335 | if (fieldValue == default)
336 | {
337 | continue;
338 | }
339 |
340 | var jsonNode = ToJsonNode(fieldValue, settings);
341 | if (fieldInfo.ContainsAttribute())
342 | {
343 | return jsonNode;
344 | }
345 |
346 | jsonObject.Add(fieldInfo.Name, jsonNode);
347 | }
348 | }
349 |
350 | return jsonObject;
351 | }
352 | }
353 | }
354 |
355 | private static JSONArray ToJsonNode(IList list, SimpleJSONParserSettings settings)
356 | {
357 | var jsonArray = new JSONArray();
358 |
359 | for (var i = 0; i < list.Count; i++)
360 | {
361 | jsonArray.Add(ToJsonNode(list[i], settings));
362 | }
363 |
364 | return jsonArray;
365 | }
366 |
367 | private static JSONObject ToJsonNode(IDictionary dict, SimpleJSONParserSettings settings)
368 | {
369 | var jsonObject = new JSONObject();
370 |
371 | foreach (var key in dict.Keys)
372 | {
373 | jsonObject.Add(key.ToString(), ToJsonNode(dict[key], settings));
374 | }
375 |
376 | return jsonObject;
377 | }
378 | }
379 | }
--------------------------------------------------------------------------------
/TranslationCommon/SimpleJSON/SimpleJSONUtils.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace TranslationCommon.SimpleJSON
4 | {
5 | public static class SimpleJSONUtils
6 | {
7 | public static IEnumerable ToEnumerable(this IEnumerator enumerator)
8 | {
9 | while ( enumerator.MoveNext() ) {
10 | yield return enumerator.Current;
11 | }
12 | }
13 |
14 | public static IEnumerable ToKeyEnumerable(this JSONNode.Enumerator enumerator)
15 | {
16 | while ( enumerator.MoveNext() ) {
17 | yield return enumerator.Current.Key;
18 | }
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/TranslationCommon/Translation/LanguageContainer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Text;
7 | using TranslationCommon.SimpleJSON;
8 | using UnityEngine;
9 | using Debug = UnityEngine.Debug;
10 |
11 | namespace TranslationCommon.Translation
12 | {
13 | ///
14 | /// Container for single language methods
15 | ///
16 | public class LanguageContainer
17 | {
18 | ///
19 | /// Settings of the language
20 | ///
21 | public LanguageSettings Settings;
22 |
23 | ///
24 | /// Translation data
25 | ///
26 | public LanguageData Translation;
27 |
28 | public Font[] Fonts;
29 |
30 | ///
31 | /// Constructor of the container with settings alread loaded into memory
32 | ///
33 | ///
34 | public LanguageContainer(LanguageSettings settings)
35 | {
36 | Settings = settings;
37 | }
38 |
39 | ///
40 | /// Load translation data using template Language data
41 | ///
42 | ///
43 | public void LoadTranslation(LanguageData template)
44 | {
45 | var translationFilePath = Path.Combine(Settings.SettingsDirectory, LanguageData.TranslationFileName);
46 | PlainTextDump(template, translationFilePath);
47 |
48 | if (Fonts == null)
49 | {
50 | Debug.Log($"Loading bundle {Settings.FontBundlePath}");
51 | AssetBundle fontBundle = AssetBundle.LoadFromFile(Settings.FontBundlePath);
52 | if (fontBundle != null)
53 | {
54 | Fonts = fontBundle.LoadAllAssets();
55 | }
56 | }
57 |
58 | if (Settings.ImportFromLegacy)
59 | {
60 | LegacyDump(template, translationFilePath);
61 | }
62 | else
63 | {
64 | CrowdinDump(template);
65 | }
66 | }
67 |
68 | ///
69 | /// Load and/or Dump Crowdin format translation
70 | ///
71 | /// Template for new language files
72 | private void CrowdinDump(LanguageData template)
73 | {
74 | Func key = proto => $"{proto.Name}";
75 | Func value = proto => proto.Translation;
76 | var translationCrowdinFilePath = Path.Combine(Settings.SettingsDirectory, LanguageData.TranslationCrowdinFileName);
77 | if (!File.Exists(translationCrowdinFilePath))
78 | {
79 | var dict = ToCrowdinDictionary(template.TranslationTable, key, value);
80 | File.WriteAllText(translationCrowdinFilePath, JSON.ToJson(dict));
81 | }
82 | else
83 | {
84 | var dictionary = JSON.FromJson>(File.ReadAllText(translationCrowdinFilePath));
85 | Translation = new LanguageData();
86 | Translation.TranslationTable = new List();
87 | foreach (var pair in dictionary)
88 | {
89 | // TODO make sure that miss matches with name and ID are handled
90 | var translationProto = TranslationProto.FromCrowdin(pair.Key, pair.Value);
91 | var match = template.TranslationTable.FirstOrDefault(proto => proto.Name == translationProto.Name);
92 | if (translationProto.Name == "string" && match != null)
93 | {
94 | translationProto.Original = match.Original;
95 | translationProto.Name = match.Name;
96 | }
97 |
98 | Translation.TranslationTable.Add(translationProto);
99 | }
100 |
101 | //Translation.UpdateTranslationItems(Settings, template);
102 | // overwrite if new translation show up
103 | var dict = ToCrowdinDictionary(Translation.TranslationTable, key, value);
104 | File.WriteAllText(translationCrowdinFilePath, JSON.ToJson(dict));
105 | }
106 | }
107 |
108 | ///
109 | /// Load and/or Dump Legacy format translation
110 | ///
111 | /// Template for new language files
112 | /// Translation file path
113 | private void LegacyDump(LanguageData template, string translationFilePath)
114 | {
115 | if (!File.Exists(translationFilePath))
116 | {
117 | File.WriteAllText(translationFilePath, JSON.ToJson(template));
118 | }
119 | else
120 | {
121 | Translation = JSON.FromJson(File.ReadAllText(translationFilePath));
122 | UpdateTranslationItems(Settings, Translation, template);
123 | // overwrite if new translation show up
124 | File.WriteAllText(translationFilePath, JSON.ToJson(Translation));
125 | }
126 | }
127 |
128 | ///
129 | /// Load and/or Dump Plain text format translation
130 | ///
131 | /// Template for new language files
132 | /// Translation file path
133 | private void PlainTextDump(LanguageData template, string translationFilePath)
134 | {
135 | if (Settings.CreateAndUpdateFromPlainTextDumpUnsafe)
136 | {
137 | var translationDumpFilePath = Path.Combine(Settings.SettingsDirectory, LanguageData.TranslationDumpFileName);
138 | if (!File.Exists(translationDumpFilePath))
139 | {
140 | var sb = new StringBuilder();
141 | foreach (var table in template.TranslationTable)
142 | {
143 | sb.AppendLine(table.Original);
144 | sb.AppendLine("-----");
145 | }
146 |
147 | File.WriteAllText(translationDumpFilePath, sb.ToString());
148 | }
149 | else
150 | {
151 | var lines = File.ReadAllText(translationDumpFilePath);
152 | var split = lines.Split(new string[] {"-----"}, StringSplitOptions.None);
153 | Translation = new LanguageData {TranslationTable = new List()};
154 | for (var index = 0; index < template.TranslationTable.Count; index++)
155 | {
156 | Translation.TranslationTable.Add(new TranslationProto(template.TranslationTable[index], split[index]
157 | .Trim('\r', '\n')));
158 | }
159 |
160 | File.WriteAllText(translationFilePath, JSON.ToJson(Translation));
161 | }
162 | }
163 | }
164 |
165 | ///
166 | /// Converts list of translation to Dictionary for Crowdin support
167 | ///
168 | /// Translation list to convert
169 | /// Key Expression
170 | /// Value Expression
171 | ///
172 | private Dictionary ToCrowdinDictionary(List translationProto, Func key, Func value)
173 | {
174 | var dict = new Dictionary();
175 | foreach (var proto in translationProto)
176 | {
177 | if (dict.ContainsKey(key(proto)))
178 | {
179 | Debugger.Break();
180 | }
181 | else
182 | {
183 | dict.Add(key(proto), value(proto));
184 | }
185 | }
186 |
187 | return dict;
188 | }
189 |
190 | ///
191 | /// Updates target file with new template data using settings
192 | ///
193 | /// Settings
194 | /// Target file
195 | /// Template file
196 | public void UpdateTranslationItems(LanguageSettings settings, LanguageData targetFile, LanguageData template)
197 | {
198 | var missMatchList = new List();
199 | var tempTranslationTable = targetFile.TranslationTable.ToList();
200 | // Find invalid or missing translations
201 | foreach (var translationProto in tempTranslationTable)
202 | {
203 | var match = template.TranslationTable.FirstOrDefault(proto => proto.Name == translationProto.Name);
204 | if (match != null)
205 | {
206 | if (match.Original != translationProto.Original)
207 | {
208 | translationProto.IsValid = false;
209 | missMatchList.Add(translationProto);
210 | ConsoleLogger.LogWarning($"Translation for {translationProto.Original} -- {translationProto.Translation} is no longer valid! This entry original meaning has changed");
211 | }
212 | else
213 | {
214 | translationProto.Original = match.Original;
215 | if (translationProto.Original.StartsWith("UI"))
216 | {
217 | translationProto.Translation = match.Original;
218 | }
219 | }
220 | }
221 | else
222 | {
223 | translationProto.IsValid = false;
224 | missMatchList.Add(translationProto);
225 | ConsoleLogger.LogWarning($"Translation for {translationProto.Original} -- {translationProto.Translation} is no longer valid! This entry was probably removed");
226 | }
227 | }
228 | // New translations
229 | foreach (var translationProto in template.TranslationTable)
230 | {
231 | var match = targetFile.TranslationTable.FirstOrDefault(proto => proto.Name == translationProto.Name);
232 | if (match == null)
233 | {
234 | missMatchList.Add(translationProto);
235 | tempTranslationTable.Add(translationProto);
236 | ConsoleLogger.LogWarning($"New translation entry for {translationProto.Original} (Upgrade from {settings.GameVersion} to {GameConfig.gameVersion.ToFullString()})");
237 | }
238 | }
239 |
240 | targetFile.TranslationTable = tempTranslationTable;
241 | settings.GameVersion = GameConfig.gameVersion.ToFullString();
242 | }
243 | }
244 | }
--------------------------------------------------------------------------------
/TranslationCommon/Translation/LanguageData.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Reflection;
4 | using UnityEngine;
5 |
6 | namespace TranslationCommon.Translation
7 | {
8 | ///
9 | /// Container for language translation data
10 | ///
11 | [Serializable]
12 | public class LanguageData
13 | {
14 | ///
15 | /// File name of legacy format
16 | ///
17 | [NonSerialized]
18 | public const string TranslationFileName = "translation.json";
19 | ///
20 | /// File name of plain text format
21 | ///
22 | [NonSerialized]
23 | public const string TranslationDumpFileName = "translation.dump.txt";
24 | ///
25 | /// File name of crowdin format
26 | ///
27 | [NonSerialized]
28 | public const string TranslationCrowdinFileName = "translation_DysonSphereProgram.json";
29 |
30 | ///
31 | /// Translation table - used for loading
32 | ///
33 | [SerializeField]
34 | public List TranslationTable;
35 |
36 | ///
37 | /// Default empty constructor
38 | ///
39 | public LanguageData()
40 | {
41 | }
42 |
43 | ///
44 | /// Copy constructor to generate new Language data from in Game Data
45 | ///
46 | /// Settings
47 | /// In game translations
48 | public LanguageData(LanguageSettings settings, ProtoSet stringProto)
49 | {
50 | settings.VersionUpdate();
51 | TranslationTable = new List(stringProto.Length);
52 | var translationDelegate = GetOriginalTextDelegate(settings);
53 | for (var i = 0; i < stringProto.dataArray.Length; i++)
54 | {
55 | var proto = stringProto.dataArray[i];
56 | TranslationProto translationProto = new TranslationProto();
57 | translationProto.IsValid = true;
58 | translationProto.Original = translationDelegate(proto);
59 | translationProto.Translation = translationDelegate(proto);
60 | translationProto.Name = proto.Name;
61 | TranslationTable.Add(translationProto);
62 | }
63 | }
64 |
65 | ///
66 | /// Delegate constructor for getting field value from original translation
67 | ///
68 | /// Language settings
69 | /// Type of data
70 | /// Resulting delegate to get string from
71 | /// Throws exception if given field info was not found in assmebly
72 | public static Func GetOriginalTextDelegate(LanguageSettings settings)
73 | {
74 | var fieldInfo = typeof(T).GetField(settings.OriginalLanguage, BindingFlags.Public | BindingFlags.Instance);
75 | if (fieldInfo == null)
76 | {
77 | throw new ArgumentException($"LanguageSettings has incorrect original translation value -- used {settings.OriginalLanguage} but it do not exists");
78 | }
79 |
80 | var d = new Func(arg => (string)fieldInfo.GetValue(arg));
81 | return param => d(param);
82 | }
83 | }
84 | }
--------------------------------------------------------------------------------
/TranslationCommon/Translation/LanguageSettings.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 |
4 | namespace TranslationCommon.Translation
5 | {
6 | ///
7 | /// Settings file
8 | ///
9 | [Serializable]
10 | public class LanguageSettings
11 | {
12 | ///
13 | /// Translation settings version
14 | ///
15 | public string Version;
16 |
17 | ///
18 | /// Game version
19 | ///
20 | public string GameVersion;
21 |
22 | ///
23 | /// Original language of the in game translation
24 | ///
25 | public string OriginalLanguage;
26 |
27 | ///
28 | /// Display name in the menu
29 | ///
30 | public string LanguageDisplayName;
31 |
32 | ///
33 | /// Should use legacy format
34 | ///
35 | public bool ImportFromLegacy;
36 |
37 | ///
38 | /// Should use plain text format
39 | ///
40 | public bool CreateAndUpdateFromPlainTextDumpUnsafe;
41 |
42 | ///
43 | /// Settings path
44 | ///
45 | public string SettingsPath { get; set; }
46 |
47 | public string FontBundlePath { get; set; }
48 |
49 | ///
50 | /// Settings directory
51 | ///
52 | public string SettingsDirectory => Path.GetDirectoryName(SettingsPath);
53 |
54 | ///
55 | /// Update settings file
56 | ///
57 | public void VersionUpdate()
58 | {
59 | var defaultVersion = "0.1.0.3";
60 | if (Version != defaultVersion) Version = defaultVersion;
61 |
62 | GameVersion = GameConfig.gameVersion.ToFullString();
63 |
64 | if (String.IsNullOrEmpty(OriginalLanguage)) OriginalLanguage = "ENUS";
65 | }
66 | }
67 | }
--------------------------------------------------------------------------------
/TranslationCommon/Translation/TranslationManager.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using DSPTranslationPlugin.UnityHarmony;
6 | using TranslationCommon.SimpleJSON;
7 | using UnityEngine;
8 |
9 | namespace TranslationCommon.Translation
10 | {
11 | public static class TranslationManager
12 | {
13 | public const string PlayerPrefsCode = "dps_translate_muchaszewski_language";
14 |
15 | private static List _langauges;
16 | private static Dictionary _translationDictionary;
17 | private static LanguageContainer _currentLanguage;
18 | private static bool _isInitialized;
19 |
20 | ///
21 | /// List of all translation languages, theirs settings and translations
22 | ///
23 | public static List Langauges
24 | {
25 | get
26 | {
27 | if (_langauges == null)
28 | {
29 | CheckForNewTranslationFolder();
30 | if (!IsInitialized)
31 | {
32 | LoadCurrentLanguage();
33 | }
34 | }
35 | return _langauges;
36 | }
37 | set => _langauges = value;
38 | }
39 |
40 | public static string SelectedLanguage { get; set; }
41 |
42 | public static LanguageContainer CurrentLanguage
43 | {
44 | get => _currentLanguage;
45 | set
46 | {
47 | _currentLanguage = value;
48 | if (_currentLanguage != null)
49 | {
50 | SetupTranslationDictionary();
51 | if (!IsInitialized)
52 | {
53 | LoadCurrentLanguage();
54 | }
55 | }
56 | }
57 | }
58 |
59 | public static bool IsInitialized
60 | {
61 | get => _isInitialized;
62 | set => _isInitialized = value;
63 | }
64 |
65 | public static Dictionary TranslationDictionary
66 | {
67 | get => _translationDictionary;
68 | private set => _translationDictionary = value;
69 | }
70 |
71 | private static void SetupTranslationDictionary()
72 | {
73 | TranslationDictionary = CurrentLanguage?.Translation.TranslationTable.ToDictionary(proto => proto.Name);
74 | }
75 |
76 | ///
77 | /// Load custom language
78 | ///
79 | public static void LoadCurrentLanguage()
80 | {
81 | if (!IsInitialized)
82 | {
83 | var stringProtoSet = LDB.strings;
84 | foreach (var languageContainer in Langauges)
85 | {
86 | if (languageContainer.Translation == null)
87 | {
88 | var templateLanguageData = new LanguageData(languageContainer.Settings, stringProtoSet);
89 | languageContainer.LoadTranslation(templateLanguageData);
90 | }
91 | }
92 |
93 | var result = PlayerPrefs.GetString(TranslationManager.PlayerPrefsCode);
94 | if (!String.IsNullOrEmpty(result))
95 | {
96 | SelectedLanguage = result;
97 | if (!Langauges.Exists(container =>
98 | container.Settings.LanguageDisplayName == SelectedLanguage))
99 | {
100 | ConsoleLogger.LogFatal($"Selected language do not exists... {result}");
101 | SelectedLanguage = null;
102 | }
103 | }
104 | IsInitialized = true;
105 | }
106 |
107 | if (!string.IsNullOrEmpty(SelectedLanguage))
108 | {
109 | var selectedContainer = Langauges.FirstOrDefault(container =>
110 | container.Settings.LanguageDisplayName == SelectedLanguage);
111 | CurrentLanguage = selectedContainer;
112 | TextFontManager.CheckFonts();
113 | TextFontManager.LoadSettings();
114 | Localization.language = Language.enUS;
115 | Localization.OnLanguageChange?.Invoke(Language.enUS);
116 |
117 |
118 | }
119 | }
120 |
121 | ///
122 | /// Translation folder settings file name
123 | ///
124 | public const string SettingsJsonFileName = "settings.json";
125 |
126 | public const string FondBundleFileName = "font_bundle";
127 | ///
128 | /// Root translation folder
129 | ///
130 | public static string TranslationDirectory => $"{Utils.ConfigPath}/Translation";
131 |
132 |
133 | ///
134 | /// Gets all languages form the translation folder
135 | ///
136 | private static void CheckForNewTranslationFolder()
137 | {
138 | if (!Directory.Exists(TranslationDirectory))
139 | {
140 | Directory.CreateDirectory(TranslationDirectory);
141 | }
142 | var directories = Directory.GetDirectories(TranslationDirectory);
143 | Langauges = new List();
144 | foreach (var directory in directories)
145 | {
146 | LanguageSettings settings;
147 | settings = GetLanguageSettings(directory);
148 |
149 | settings.SettingsPath = Path.Combine(directory, SettingsJsonFileName);
150 | Langauges.Add(new LanguageContainer(settings));
151 | }
152 | }
153 |
154 | ///
155 | /// Get language settings
156 | ///
157 | /// Directory name
158 | ///
159 | private static LanguageSettings GetLanguageSettings(string directory)
160 | {
161 | LanguageSettings settings = null;
162 |
163 | string translationMain = null;
164 | string bundlePath = "";
165 | foreach (var file in Directory.GetFiles(directory))
166 | {
167 | if (file.Contains(SettingsJsonFileName)) translationMain = file;
168 | if (file.Contains(FondBundleFileName)) bundlePath = file;
169 | }
170 |
171 | if (translationMain == null)
172 | {
173 | settings = new LanguageSettings()
174 | {
175 | LanguageDisplayName = Path.GetFileName(directory),
176 | };
177 | File.WriteAllText(Path.Combine(directory, SettingsJsonFileName), JSON.ToJson(settings, true));
178 | }
179 | else
180 | {
181 | settings = JSON.FromJson(File.ReadAllText(translationMain));
182 | settings.FontBundlePath = bundlePath;
183 | settings.VersionUpdate();
184 | // Overwrite file with potential new settings
185 | File.WriteAllText(Path.Combine(directory, SettingsJsonFileName), JSON.ToJson(settings, true));
186 | }
187 |
188 | return settings;
189 | }
190 | }
191 | }
--------------------------------------------------------------------------------
/TranslationCommon/Translation/TranslationProto.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace TranslationCommon.Translation
4 | {
5 | [Serializable]
6 | public class TranslationProto
7 | {
8 | public bool IsValid;
9 |
10 | public string Name;
11 |
12 | public string Original;
13 | public string Translation;
14 |
15 | public string GetTranslation()
16 | {
17 | return Translation;
18 | }
19 |
20 | public TranslationProto(TranslationProto proto, string translation)
21 | {
22 | Name = proto.Name;
23 | Original = proto.Original;
24 | Translation = translation;
25 | }
26 |
27 | public static TranslationProto FromCrowdin(string name, string translation)
28 | {
29 | TranslationProto result = new TranslationProto();
30 | result.Name = name;
31 | result.Translation = translation;
32 | return result;
33 | }
34 |
35 | public TranslationProto(string name, int id, string original, string translation)
36 | {
37 | Name = name;
38 | Original = original;
39 | Translation = translation;
40 | }
41 |
42 | public TranslationProto()
43 | {
44 |
45 | }
46 | }
47 | }
--------------------------------------------------------------------------------
/TranslationCommon/TranslationCommon.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net472
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/TranslationCommon/Utils.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.IO;
3 | using System.Linq;
4 | using System.Reflection;
5 | using TranslationCommon.SimpleJSON;
6 | using UnityEngine;
7 |
8 | namespace TranslationCommon
9 | {
10 | public static class Utils
11 | {
12 | private static readonly string AssemblyLocation = Assembly.GetCallingAssembly().Location;
13 |
14 | public static string PluginFolderName = Path.GetDirectoryName(AssemblyLocation);
15 | ///
16 | /// Plugin path
17 | ///
18 | public static string PluginPath = Path.GetDirectoryName(AssemblyLocation);
19 |
20 | public static string ConfigPath = Application.dataPath;
21 | }
22 | }
--------------------------------------------------------------------------------
/TranslationCommon/WildcardMatch.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 All Rights Reserved
3 | //
4 | // H.A. Sullivan
5 | // 04/11/2016
6 | // Wildcard matching string extension method
7 | // MIT License
8 | //
9 | // Copyright(c) [2016]
10 | // [H.A. Sullivan]
11 | //
12 | // Permission is hereby granted, free of charge, to any person obtaining a copy
13 | // of this software and associated documentation files (the "Software"), to deal
14 | // in the Software without restriction, including without limitation the rights
15 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 | // copies of the Software, and to permit persons to whom the Software is
17 | // furnished to do so, subject to the following conditions:
18 | //
19 | // The above copyright notice and this permission notice shall be included in all
20 | // copies or substantial portions of the Software.
21 | //
22 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 | // SOFTWARE.
29 | //
30 |
31 | using System.Collections.Generic;
32 |
33 | public static class WildcardMatch
34 | {
35 | public static bool EqualsWildcard(this string text, string wildcardString)
36 | {
37 | var isLike = true;
38 | byte matchCase = 0;
39 | char[] filter;
40 | char[] reversedFilter;
41 | char[] reversedWord;
42 | char[] word;
43 | var currentPatternStartIndex = 0;
44 | var lastCheckedHeadIndex = 0;
45 | var lastCheckedTailIndex = 0;
46 | var reversedWordIndex = 0;
47 | var reversedPatterns = new List();
48 |
49 | if (text == null || wildcardString == null)
50 | {
51 | return false;
52 | }
53 |
54 | word = text.ToCharArray();
55 | filter = wildcardString.ToCharArray();
56 |
57 | //Set which case will be used (0 = no wildcards, 1 = only ?, 2 = only *, 3 = both ? and *
58 | for (var i = 0; i < filter.Length; i++)
59 | {
60 | if (filter[i] == '?')
61 | {
62 | matchCase += 1;
63 | break;
64 | }
65 | }
66 |
67 | for (var i = 0; i < filter.Length; i++)
68 | {
69 | if (filter[i] == '*')
70 | {
71 | matchCase += 2;
72 | break;
73 | }
74 | }
75 |
76 | if ((matchCase == 0 || matchCase == 1) && word.Length != filter.Length)
77 | {
78 | return false;
79 | }
80 |
81 | switch (matchCase)
82 | {
83 | case 0:
84 | isLike = text == wildcardString;
85 | break;
86 |
87 | case 1:
88 | for (var i = 0; i < text.Length; i++)
89 | {
90 | if (word[i] != filter[i] && filter[i] != '?')
91 | {
92 | isLike = false;
93 | }
94 | }
95 |
96 | break;
97 |
98 | case 2:
99 | //Search for matches until first *
100 | for (var i = 0; i < filter.Length; i++)
101 | {
102 | if (filter[i] != '*')
103 | {
104 | if (filter[i] != word[i])
105 | {
106 | return false;
107 | }
108 | }
109 | else
110 | {
111 | lastCheckedHeadIndex = i;
112 | break;
113 | }
114 | }
115 |
116 | //Search Tail for matches until first *
117 | for (var i = 0; i < filter.Length; i++)
118 | {
119 | if (filter[filter.Length - 1 - i] != '*')
120 | {
121 | if (word.Length == i)
122 | {
123 | return false;
124 | }
125 | if (filter[filter.Length - 1 - i] != word[word.Length - 1 - i])
126 | {
127 | return false;
128 | }
129 | }
130 | else
131 | {
132 | lastCheckedTailIndex = i;
133 | break;
134 | }
135 | }
136 |
137 |
138 | //Create a reverse word and filter for searching in reverse. The reversed word and filter do not include already checked chars
139 | reversedWord = new char[word.Length - lastCheckedHeadIndex - lastCheckedTailIndex];
140 | reversedFilter = new char[filter.Length - lastCheckedHeadIndex - lastCheckedTailIndex];
141 |
142 | for (var i = 0; i < reversedWord.Length; i++)
143 | {
144 | reversedWord[i] = word[word.Length - (i + 1) - lastCheckedTailIndex];
145 | }
146 |
147 | for (var i = 0; i < reversedFilter.Length; i++)
148 | {
149 | reversedFilter[i] = filter[filter.Length - (i + 1) - lastCheckedTailIndex];
150 | }
151 |
152 | //Cut up the filter into seperate patterns, exclude * as they are not longer needed
153 | for (var i = 0; i < reversedFilter.Length; i++)
154 | {
155 | if (reversedFilter[i] == '*')
156 | {
157 | if (i - currentPatternStartIndex > 0)
158 | {
159 | var pattern = new char[i - currentPatternStartIndex];
160 | for (var j = 0; j < pattern.Length; j++)
161 | {
162 | pattern[j] = reversedFilter[currentPatternStartIndex + j];
163 | }
164 |
165 | reversedPatterns.Add(pattern);
166 | }
167 |
168 | currentPatternStartIndex = i + 1;
169 | }
170 | }
171 |
172 | //Search for the patterns
173 | for (var i = 0; i < reversedPatterns.Count; i++)
174 | {
175 | for (var j = 0; j < reversedPatterns[i].Length; j++)
176 | {
177 | if (reversedPatterns[i].Length - 1 - j > reversedWord.Length - 1 - reversedWordIndex)
178 | {
179 | return false;
180 | }
181 |
182 | if (reversedPatterns[i][j] != reversedWord[reversedWordIndex + j])
183 | {
184 | reversedWordIndex += 1;
185 | j = -1;
186 | }
187 | else
188 | {
189 | if (j == reversedPatterns[i].Length - 1)
190 | {
191 | reversedWordIndex = reversedWordIndex + reversedPatterns[i].Length;
192 | }
193 | }
194 | }
195 | }
196 |
197 | break;
198 |
199 | case 3:
200 | //Same as Case 2 except ? is considered a match
201 | //Search Head for matches util first *
202 | for (var i = 0; i < filter.Length; i++)
203 | {
204 | if (filter[i] != '*')
205 | {
206 | if (filter[i] != word[i] && filter[i] != '?')
207 | {
208 | return false;
209 | }
210 | }
211 | else
212 | {
213 | lastCheckedHeadIndex = i;
214 | break;
215 | }
216 | }
217 |
218 | //Search Tail for matches until first *
219 | for (var i = 0; i < filter.Length; i++)
220 | {
221 | if (filter[filter.Length - 1 - i] != '*')
222 | {
223 | if (filter[filter.Length - 1 - i] != word[word.Length - 1 - i] &&
224 | filter[filter.Length - 1 - i] != '?')
225 | {
226 | return false;
227 | }
228 | }
229 | else
230 | {
231 | lastCheckedTailIndex = i;
232 | break;
233 | }
234 | }
235 |
236 | // Reverse and trim word and filter
237 | reversedWord = new char[word.Length - lastCheckedHeadIndex - lastCheckedTailIndex];
238 | reversedFilter = new char[filter.Length - lastCheckedHeadIndex - lastCheckedTailIndex];
239 |
240 | for (var i = 0; i < reversedWord.Length; i++)
241 | {
242 | reversedWord[i] = word[word.Length - (i + 1) - lastCheckedTailIndex];
243 | }
244 |
245 | for (var i = 0; i < reversedFilter.Length; i++)
246 | {
247 | reversedFilter[i] = filter[filter.Length - (i + 1) - lastCheckedTailIndex];
248 | }
249 |
250 | for (var i = 0; i < reversedFilter.Length; i++)
251 | {
252 | if (reversedFilter[i] == '*')
253 | {
254 | if (i - currentPatternStartIndex > 0)
255 | {
256 | var pattern = new char[i - currentPatternStartIndex];
257 | for (var j = 0; j < pattern.Length; j++)
258 | {
259 | pattern[j] = reversedFilter[currentPatternStartIndex + j];
260 | }
261 |
262 | reversedPatterns.Add(pattern);
263 | }
264 |
265 | currentPatternStartIndex = i + 1;
266 | }
267 | }
268 |
269 | //Search for the patterns
270 | for (var i = 0; i < reversedPatterns.Count; i++)
271 | {
272 | for (var j = 0; j < reversedPatterns[i].Length; j++)
273 | {
274 | if (reversedPatterns[i].Length - 1 - j > reversedWord.Length - 1 - reversedWordIndex)
275 | {
276 | return false;
277 | }
278 |
279 |
280 | if (reversedPatterns[i][j] != '?' &&
281 | reversedPatterns[i][j] != reversedWord[reversedWordIndex + j])
282 | {
283 | reversedWordIndex += 1;
284 | j = -1;
285 | }
286 | else
287 | {
288 | if (j == reversedPatterns[i].Length - 1)
289 | {
290 | reversedWordIndex = reversedWordIndex + reversedPatterns[i].Length;
291 | }
292 | }
293 | }
294 | }
295 |
296 | break;
297 | }
298 |
299 | return isLike;
300 | }
301 |
302 | public static bool EqualsWildcard(this string text, string wildcardString, bool ignoreCase)
303 | {
304 | if (ignoreCase)
305 | {
306 | return text.ToLower().EqualsWildcard(wildcardString.ToLower());
307 | }
308 |
309 | return text.EqualsWildcard(wildcardString);
310 | }
311 | }
--------------------------------------------------------------------------------
/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Muchaszewski/DSP_TranslationMod/bc74e3ae3500ce3c4fb86b7191a9cc5b9e56ee2f/icon.png
--------------------------------------------------------------------------------
/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "DSPTranslationPlugin",
3 | "version_number": "0.5.2",
4 | "website_url": "https://github.com/Muchaszewski/DSP_TranslationMod",
5 | "description": "Dyson sphere translation plugin! Translations download form crowdin https://crowdin.com/translate/dyson-sphere-program/14#",
6 | "dependencies": [
7 | "xiaoye97-BepInEx-5.4.17"
8 | ]
9 | }
--------------------------------------------------------------------------------
/thunderstore.toml:
--------------------------------------------------------------------------------
1 | [config]
2 | schemaVersion = "0.0.1"
3 |
4 | [package]
5 | namespace = "Muchaszewski"
6 | name = "DSPTranslationPlugin"
7 | versionNumber = "0.5.2"
8 | description = "Dyson sphere translation plugin! Translations download form crowdin https://crowdin.com/translate/dyson-sphere-program/14#"
9 | websiteUrl = "https://github.com/Muchaszewski/DSP_TranslationMod"
10 | containsNsfwContent = false
11 |
12 | [package.dependencies]
13 | "xiaoye97-BepInEx" = "5.4.17"
14 |
15 | [publish]
16 | repository = "https://thunderstore.io"
17 | communities = ["dyson-sphere-program"]
18 | categories = []
--------------------------------------------------------------------------------