From e86693a494466ebf84de4cbe594c4b2a4dae4c38 Mon Sep 17 00:00:00 2001
From: yangzhi <@4F!xZpJwly&KbWq>
Date: Mon, 10 Feb 2020 16:08:56 +0800
Subject: [PATCH] first commit
---
.gitignore | 253 ++++++++++++++++++++
LICENSE.txt | 165 +++++++++++++
ModConfig.cs | 57 +++++
ModEntry.cs | 50 ++++
README.md | 44 ++++
Rewrites/LanguageSelectionMobileRewrites.cs | 233 ++++++++++++++++++
Rewrites/LocalizedContentManagerRewrites.cs | 64 +++++
Rewrites/SpriteTextRewrites.cs | 64 +++++
Rewrites/StartupPreferencesRewrites.cs | 47 ++++
Rewrites/TitleContainerRewrites.cs | 51 ++++
SMAPI.Mods.CustomLocalization.csproj | 33 +++
SMAPI.Mods.CustomLocalization.sln | 25 ++
manifest.json | 9 +
13 files changed, 1095 insertions(+)
create mode 100644 .gitignore
create mode 100644 LICENSE.txt
create mode 100644 ModConfig.cs
create mode 100644 ModEntry.cs
create mode 100644 README.md
create mode 100644 Rewrites/LanguageSelectionMobileRewrites.cs
create mode 100644 Rewrites/LocalizedContentManagerRewrites.cs
create mode 100644 Rewrites/SpriteTextRewrites.cs
create mode 100644 Rewrites/StartupPreferencesRewrites.cs
create mode 100644 Rewrites/TitleContainerRewrites.cs
create mode 100644 SMAPI.Mods.CustomLocalization.csproj
create mode 100644 SMAPI.Mods.CustomLocalization.sln
create mode 100644 manifest.json
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..bb64d4b
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,253 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+
+# User-specific files
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+
+# Visual Studio 2015 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUNIT
+*.VisualState.xml
+TestResult.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# DNX
+project.lock.json
+artifacts/
+
+*_i.c
+*_p.c
+*_i.h
+*.ilk
+*.meta
+*.obj
+*.pch
+*.pdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# JustCode is a .NET coding add-in
+.JustCode
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# TODO: Comment the next line if you want to checkin your web deploy settings
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# The packages folder can be ignored because of Package Restore
+packages/
+**/packages/*
+# except build/, which is used as an MSBuild target.
+!**/packages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/packages/repositories.config
+# NuGet v3's project.json files produces more ignoreable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.pfx
+*.publishsettings
+node_modules/
+orleans.codegen.cs
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+
+# SQL Server files
+*.mdf
+*.ldf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# JetBrains Rider
+.idea/
+*.sln.iml
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..02bbb60
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,165 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
\ No newline at end of file
diff --git a/ModConfig.cs b/ModConfig.cs
new file mode 100644
index 0000000..e9d50bc
--- /dev/null
+++ b/ModConfig.cs
@@ -0,0 +1,57 @@
+using StardewValley;
+
+namespace StardewModdingAPI.Mods.CustomLocalization
+{
+ public class ModConfig
+ {
+ public sbyte OriginLocaleCount = 11;
+ public int CurrentLanguageCode = (int)LocalizedContentManager.CurrentLanguageCode;
+ public Locale[] locales { get; set;} = new Locale[] {
+ new Locale("Chinese", "简体中文", 3, "zh-CN", false, "Fonts\\Chinese", 1.5f)
+ };
+ public class Locale
+ {
+ public string Name;
+ public string DisplayName;
+ public int CodeEnum;
+ public string LocaleCode;
+ public bool IsLatin;
+ public string FontFileName;
+ public float FontPixelZoom;
+
+ public Locale(string name, string displayName, int codeEnum, string localeCode, bool isLatin, string fontFileName, float fontPixelZoom)
+ {
+ this.Name = name;
+ this.DisplayName = displayName;
+ this.CodeEnum = codeEnum;
+ this.LocaleCode = localeCode;
+ this.IsLatin = isLatin;
+ this.FontFileName = fontFileName;
+ this.FontPixelZoom = fontPixelZoom;
+ }
+
+ }
+ public Locale GetByName(string name)
+ {
+ foreach (ModConfig.Locale locale in ModEntry.ModConfig.locales)
+ {
+ if (locale.Name == name)
+ {
+ return locale;
+ }
+ }
+ return null;
+ }
+ public Locale GetByCode(int codeEnum)
+ {
+ foreach (ModConfig.Locale locale in ModEntry.ModConfig.locales)
+ {
+ if (locale.CodeEnum == codeEnum)
+ {
+ return locale;
+ }
+ }
+ return null;
+ }
+ }
+}
diff --git a/ModEntry.cs b/ModEntry.cs
new file mode 100644
index 0000000..3d931b5
--- /dev/null
+++ b/ModEntry.cs
@@ -0,0 +1,50 @@
+using System.Collections.Generic;
+using System.Reflection;
+using Harmony;
+using StardewValley;
+using StardewValley.BellsAndWhistles;
+
+namespace StardewModdingAPI.Mods.CustomLocalization
+{
+ public class ModEntry : Mod
+ {
+ public static ModConfig ModConfig;
+
+ public static IMonitor monitor;
+
+ public static string ModPath;
+
+ private static ModEntry Instance;
+
+ public static void SaveConfig()
+ {
+ Instance.Helper.WriteConfig(ModConfig);
+ }
+
+ public override void Entry(IModHelper helper)
+ {
+ Instance = this;
+ ModConfig = helper.ReadConfig();
+ ModPath = helper.DirectoryPath;
+ monitor = this.Monitor;
+ HarmonyInstance harmony = HarmonyInstance.Create("zaneyork.CustomLocalization");
+ harmony.PatchAll(Assembly.GetExecutingAssembly());
+
+ if (ModConfig.CurrentLanguageCode > ModConfig.OriginLocaleCount)
+ {
+ monitor.Log($"Restore locale to : {ModConfig.CurrentLanguageCode}");
+ LocalizedContentManager.CurrentLanguageCode = (LocalizedContentManager.LanguageCode)ModConfig.CurrentLanguageCode;
+ }
+ else
+ {
+ Dictionary dictionary = this.Helper.Reflection.GetField>(Game1.content, "_localizedAsset").GetValue();
+ dictionary.Clear();
+ this.Helper.Reflection.GetMethod(Game1.game1, "TranslateFields").Invoke();
+ if (!LocalizedContentManager.CurrentLanguageLatin)
+ {
+ this.Helper.Reflection.GetMethod(typeof(SpriteText), "OnLanguageChange").Invoke(LocalizedContentManager.CurrentLanguageCode);
+ }
+ }
+ }
+ }
+}
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..61e8fda
--- /dev/null
+++ b/README.md
@@ -0,0 +1,44 @@
+# Custom Localization #
+
+Custom Localization is a Localization Mod for Stardew Valley Android only.
+
+## Description ##
+- For user
+
+Extract mod into Mods folder, put localization xnb resources into Content folder.
+The mod will load Content folder and add or replace xnb files into game's own content dynamically without needs of modify game's apk.
+
+- For developer
+
+Typically mod's config file looks like this:
+
+```json
+{
+ "OriginLocaleCount": 11,
+ "CurrentLanguageCode": 3,
+ "locales": [
+ {
+ "Name": "Chinese",
+ "DisplayName": "简体中文",
+ "CodeEnum": 3,
+ "LocaleCode": "zh-CN",
+ "IsLatin": false,
+ "FontFileName": "Fonts\\Chinese",
+ "FontPixelZoom": 1.5
+ }
+ ]
+}
+```
+| Config Name | Description |
+| ------------ | ------------ |
+| OriginLocaleCount | Game default support language count, do not modify |
+| CurrentLanguageCode | User current selected language code enum value |
+| CurrentLanguageCode | User current selected language code enum value |
+| locales | Mod manually added locales list |
+| Name | Locale name, make sure it's unique |
+| DisplayName | Locale display name |
+| CodeEnum | Locale enum value, game has taken 0 to 11|
+| LocaleCode | Locale country code, also it is xnb file's suffix name |
+| IsLatin | If the locale is latin based locale, which didn't have to provide font |
+| FontFileName | Font's asset name |
+| FontPixelZoom | Font's zoom scale |
diff --git a/Rewrites/LanguageSelectionMobileRewrites.cs b/Rewrites/LanguageSelectionMobileRewrites.cs
new file mode 100644
index 0000000..aaab117
--- /dev/null
+++ b/Rewrites/LanguageSelectionMobileRewrites.cs
@@ -0,0 +1,233 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Reflection.Emit;
+using Harmony;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using StardewValley;
+using StardewValley.Menus;
+using static StardewValley.LocalizedContentManager;
+
+namespace StardewModdingAPI.Mods.CustomLocalization.Rewrites
+{
+ public class LanguageSelectionMobileRewrites
+ {
+ [HarmonyPatch(typeof(LanguageSelectionMobile))]
+ [HarmonyPatch("SetupButtons")]
+ public class SetupButtonsRewrite
+ {
+ [HarmonyTranspiler]
+ public static IEnumerable Transpiler(IEnumerable instructions)
+ {
+ var codes = new List(instructions);
+ for (int i = 0; i < codes.Count(); i++)
+ {
+ if (codes[i].opcode == OpCodes.Ldc_I4_S && (sbyte)codes[i].operand == ModEntry.ModConfig.OriginLocaleCount)
+ {
+ if(i + 3 < codes.Count() && codes[i + 3].opcode == OpCodes.Mul)
+ {
+ codes[i].operand = (sbyte)(ModEntry.ModConfig.OriginLocaleCount + ModEntry.ModConfig.locales.Length);
+ }
+ }
+ else if (codes[i].opcode == OpCodes.Ldc_R4 && (float)codes[i].operand == ModEntry.ModConfig.OriginLocaleCount)
+ {
+ codes[i].operand = (float)(ModEntry.ModConfig.OriginLocaleCount + ModEntry.ModConfig.locales.Length);
+ }
+ }
+ return codes.AsEnumerable();
+ }
+ }
+ [HarmonyPatch(typeof(LanguageSelectionMobile))]
+ [HarmonyPatch("setCurrentItemIndex")]
+ public class SetCurrentItemIndexRewrite
+ {
+ [HarmonyPrefix]
+ public static void Prefix(LanguageSelectionMobile __instance)
+ {
+ Rectangle mainBox = (Rectangle)__instance.GetType().GetField("mainBox", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).GetValue(__instance);
+ int buttonHeight = (int)__instance.GetType().GetField("buttonHeight", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).GetValue(__instance);
+ for(short i = 0; i < ModEntry.ModConfig.locales.Length; i++)
+ {
+ ModConfig.Locale locale = ModEntry.ModConfig.locales[i];
+ __instance.languages.Add(new ClickableComponent(
+ new Rectangle(mainBox.X + 0x10, (mainBox.Y + 0x10) + (buttonHeight * ModEntry.ModConfig.OriginLocaleCount + i), __instance.buttonWidth, buttonHeight),
+ locale.Name, null));
+ if ((int)(CurrentLanguageCode) == locale.CodeEnum)
+ {
+ __instance.GetType().GetField("languageCodeName", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).SetValue(__instance, locale.Name);
+ }
+ }
+ }
+ }
+ [HarmonyPatch(typeof(LanguageSelectionMobile))]
+ [HarmonyPatch("releaseLeftClick")]
+ public class ReleaseLeftClickRewrite
+ {
+ [HarmonyPrefix]
+ public static bool Prefix(LanguageSelectionMobile __instance, int x, int y)
+ {
+ MobileScrollbox scrollArea = (MobileScrollbox)__instance.GetType().GetField("scrollArea", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).GetValue(__instance);
+ if (scrollArea == null || !scrollArea.havePanelScrolled)
+ {
+ foreach (ClickableComponent language in __instance.languages)
+ {
+ if (language.containsPoint(x, y))
+ {
+ __instance.GetType().GetField("languageChosen", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).SetValue(__instance, true);
+ Game1.playSound("select");
+ switch (language.name)
+ {
+ case "English":
+ CurrentLanguageCode = LanguageCode.en;
+ break;
+ case "French":
+ CurrentLanguageCode = LanguageCode.fr;
+ break;
+ case "German":
+ CurrentLanguageCode = LanguageCode.de;
+ break;
+ case "Hungarian":
+ CurrentLanguageCode = LanguageCode.hu;
+ break;
+ case "Italian":
+ CurrentLanguageCode = LanguageCode.it;
+ break;
+ case "Japanese":
+ CurrentLanguageCode = LanguageCode.ja;
+ break;
+ case "Korean":
+ CurrentLanguageCode = LanguageCode.ko;
+ break;
+ case "Portuguese":
+ CurrentLanguageCode = LanguageCode.pt;
+ break;
+ case "Russian":
+ CurrentLanguageCode = LanguageCode.ru;
+ break;
+ case "Spanish":
+ CurrentLanguageCode = LanguageCode.es;
+ break;
+ case "Turkish":
+ CurrentLanguageCode = LanguageCode.tr;
+ break;
+ default:
+ ModConfig.Locale locale = ModEntry.ModConfig.GetByName(language.name);
+ if(locale != null)
+ {
+ CurrentLanguageCode = (LocalizedContentManager.LanguageCode)locale.CodeEnum;
+ break;
+ }
+ else
+ {
+ CurrentLanguageCode = LanguageCode.en;
+ }
+ break;
+ }
+ __instance.exitThisMenu(true);
+ }
+ }
+ }
+ if (scrollArea == null)
+ return false;
+ scrollArea.releaseLeftClick(x, y);
+ return false;
+ }
+ }
+ [HarmonyPatch(typeof(LanguageSelectionMobile))]
+ [HarmonyPatch("draw")]
+ [HarmonyPatch(new Type[] { typeof(SpriteBatch) })]
+ public class DrawRewrite
+ {
+ [HarmonyPrefix]
+ public static bool Prefix(LanguageSelectionMobile __instance, SpriteBatch b)
+ {
+ Utility.getTopLeftPositionForCenteringOnScreen(__instance.width, __instance.height - 100, 0, 0);
+ SpriteBatch spriteBatch = b;
+ Texture2D fadeToBlackRect = Game1.fadeToBlackRect;
+ Viewport viewport = Game1.graphics.GraphicsDevice.Viewport;
+ Rectangle bounds = viewport.Bounds;
+ Color color = Color.Multiply(Color.Black, 0.6f);
+ spriteBatch.Draw(fadeToBlackRect, bounds, color);
+ Rectangle mainBox = (Rectangle)__instance.GetType().GetField("mainBox", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).GetValue(__instance);
+ IClickableMenu.drawTextureBox(b, (int)mainBox.X, (int)mainBox.Y, (int)mainBox.Width, (int)mainBox.Height, Color.White);
+ MobileScrollbox scrollArea = (MobileScrollbox)__instance.GetType().GetField("scrollArea", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).GetValue(__instance);
+ if (scrollArea != null)
+ {
+ MobileScrollbar newScrollbar = (MobileScrollbar)__instance.GetType().GetField("newScrollbar", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).GetValue(__instance);
+ newScrollbar.draw(b);
+ scrollArea.setUpForScrollBoxDrawing(b, 1f);
+ }
+ foreach (ClickableComponent language in __instance.languages)
+ {
+ int num1 = -1;
+ switch (language.name)
+ {
+ case "English":
+ num1 = 0;
+ break;
+ case "Spanish":
+ num1 = 1;
+ break;
+ case "Portuguese":
+ num1 = 2;
+ break;
+ case "Russian":
+ num1 = 3;
+ break;
+ case "Chinese":
+ num1 = 4;
+ break;
+ case "Japanese":
+ num1 = 5;
+ break;
+ case "German":
+ num1 = 6;
+ break;
+ case "French":
+ num1 = 7;
+ break;
+ case "Korean":
+ num1 = 8;
+ break;
+ case "Turkish":
+ num1 = 9;
+ break;
+ case "Italian":
+ num1 = 10;
+ break;
+ case "Hungarian":
+ num1 = 11;
+ break;
+ }
+ if(num1 >= 0)
+ {
+ int num2 = (num1 <= 6 ? num1 * 78 : (num1 - 7) * 78) + (language.label != null ? 39 : 0);
+ int num3 = num1 > 6 ? 174 : 0;
+ Texture2D texture = (Texture2D)__instance.GetType().GetField("texture", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).GetValue(__instance);
+ b.Draw(texture, language.bounds, new Rectangle?(new Rectangle(num3, num2, 174, 40)), Color.White, 0.0f, new Vector2(0.0f, 0.0f), (SpriteEffects)0, 0.0f);
+ }
+ else
+ {
+ Texture2D texture = (Texture2D)__instance.GetType().GetField("texture", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).GetValue(__instance);
+ b.Draw(texture, language.bounds, new Rectangle?(new Rectangle(0, language.label != null ? 39 : 0, 174, 40)), Color.White, 0.0f, new Vector2(0.0f, 0.0f), 0, 0.0f);
+ int xOffset = 18 * language.bounds.Width / 174;
+ int yOffset = 8 * language.bounds.Height / 40;
+ b.Draw(texture, new Rectangle(language.bounds.X + xOffset, language.bounds.Y + yOffset, language.bounds.Width - xOffset * 2, language.bounds.Height - yOffset * 2), new Rectangle(18, language.label != null ? 47 : 8, 14, 24), Color.White, 0.0f, new Vector2(0.0f, 0.0f), 0, 0.0f);
+ ModConfig.Locale locale = ModEntry.ModConfig.GetByName(language.name);
+ Vector2 measureSize = Game1.dialogueFont.MeasureString(locale.DisplayName);
+ b.DrawString(Game1.dialogueFont, locale.DisplayName, new Vector2(language.bounds.X + (language.bounds.Width - measureSize.X) / 2, language.bounds.Y + (language.bounds.Height - measureSize.Y) / 2), new Color(206, 82, 82));
+ }
+ }
+ if (scrollArea != null)
+ scrollArea.finishScrollBoxDrawing(b, 1f);
+ if ((__instance.upperRightCloseButton != null) && __instance.shouldDrawCloseButton())
+ {
+ __instance.upperRightCloseButton.draw(b);
+ }
+ return false;
+ }
+ }
+ }
+}
diff --git a/Rewrites/LocalizedContentManagerRewrites.cs b/Rewrites/LocalizedContentManagerRewrites.cs
new file mode 100644
index 0000000..367b863
--- /dev/null
+++ b/Rewrites/LocalizedContentManagerRewrites.cs
@@ -0,0 +1,64 @@
+using System;
+using System.IO;
+using System.Reflection;
+using Harmony;
+using Microsoft.Xna.Framework;
+using StardewValley;
+using static StardewValley.LocalizedContentManager;
+
+namespace StardewModdingAPI.Mods.CustomLocalization.Rewrites
+{
+ public class LocalizedContentManagerRewrites
+ {
+ [HarmonyPatch(typeof(LocalizedContentManager))]
+ [HarmonyPatch("LanguageCodeString")]
+ public class LanguageCodeStringRewrite
+ {
+ [HarmonyPrefix]
+ public static bool Prefix(LanguageCode code, ref string __result)
+ {
+ switch (code)
+ {
+ case LanguageCode.ja:
+ case LanguageCode.ru:
+ case LanguageCode.pt:
+ case LanguageCode.es:
+ case LanguageCode.de:
+ case LanguageCode.th:
+ case LanguageCode.fr:
+ case LanguageCode.ko:
+ case LanguageCode.it:
+ case LanguageCode.tr:
+ case LanguageCode.hu:
+ return true;
+ default:
+ foreach (ModConfig.Locale locale in ModEntry.ModConfig.locales)
+ {
+ if (locale.CodeEnum == (int)code)
+ {
+ __result = locale.LocaleCode;
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+ }
+ [HarmonyPatch(typeof(LocalizedContentManager))]
+ [HarmonyPatch("get_CurrentLanguageLatin")]
+ public class GetCurrentLanguageLatinRewrite
+ {
+ [HarmonyPrefix]
+ public static bool Prefix(ref bool __result)
+ {
+ ModConfig.Locale locale = ModEntry.ModConfig.GetByCode((int)LocalizedContentManager.CurrentLanguageCode);
+ if(locale != null)
+ {
+ __result = locale.IsLatin;
+ return false;
+ }
+ return true;
+ }
+ }
+ }
+}
diff --git a/Rewrites/SpriteTextRewrites.cs b/Rewrites/SpriteTextRewrites.cs
new file mode 100644
index 0000000..b4dee94
--- /dev/null
+++ b/Rewrites/SpriteTextRewrites.cs
@@ -0,0 +1,64 @@
+using System.Reflection;
+using Harmony;
+using StardewValley;
+using StardewValley.BellsAndWhistles;
+
+namespace StardewModdingAPI.Mods.CustomLocalization.Rewrites
+{
+ public class SpriteTextRewrites
+ {
+ private static void LoadFont()
+ {
+ ModConfig.Locale locale = ModEntry.ModConfig.GetByCode((int)LocalizedContentManager.CurrentLanguageCode);
+ if (locale != null && !locale.IsLatin)
+ {
+ bool configChanged = false;
+ if(locale.FontFileName == null)
+ {
+ locale.FontFileName = "Fonts\\Chinese";
+ configChanged = true;
+ }
+ if(locale.FontPixelZoom <= 0)
+ {
+ locale.FontPixelZoom = 1.5f;
+ configChanged = true;
+ }
+ if (configChanged)
+ {
+ ModEntry.SaveConfig();
+ }
+ object fontFile = typeof(SpriteText).GetMethod("loadFont", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static).Invoke(null, new object[] { locale.FontFileName });
+ typeof(SpriteText).GetField("FontFile", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static).SetValue(null, fontFile);
+ SpriteText.fontPixelZoom = locale.FontPixelZoom;
+ }
+ }
+ [HarmonyPatch(typeof(SpriteText))]
+ [HarmonyPatch("setUpCharacterMap")]
+ public class SetUpCharacterMapRewrite
+ {
+ [HarmonyPrefix]
+ public static void Prefix()
+ {
+ if (!LocalizedContentManager.CurrentLanguageLatin)
+ {
+ if(typeof(SpriteText).GetField("_characterMap", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static).GetValue(null) == null)
+ {
+ LoadFont();
+ }
+ }
+ }
+
+
+ }
+ [HarmonyPatch(typeof(SpriteText))]
+ [HarmonyPatch("OnLanguageChange")]
+ public class OnLanguageChangeRewrite
+ {
+ [HarmonyPrefix]
+ public static void Prefix()
+ {
+ LoadFont();
+ }
+ }
+ }
+}
diff --git a/Rewrites/StartupPreferencesRewrites.cs b/Rewrites/StartupPreferencesRewrites.cs
new file mode 100644
index 0000000..b3d66c9
--- /dev/null
+++ b/Rewrites/StartupPreferencesRewrites.cs
@@ -0,0 +1,47 @@
+using System.IO;
+using System.Reflection;
+using Harmony;
+using StardewValley;
+
+namespace StardewModdingAPI.Mods.CustomLocalization.Rewrites
+{
+ public class StartupPreferencesRewrites
+ {
+ [HarmonyPatch(typeof(StartupPreferences))]
+ [HarmonyPatch("writeSettings")]
+ public class WriteSettingsRewrite
+ {
+ [HarmonyPrefix]
+ public static void Prefix(StartupPreferences __instance)
+ {
+ ModEntry.ModConfig.CurrentLanguageCode = (int)LocalizedContentManager.CurrentLanguageCode;
+ ModEntry.SaveConfig();
+ if (ModEntry.ModConfig.CurrentLanguageCode > ModEntry.ModConfig.OriginLocaleCount)
+ {
+ typeof(StartupPreferences).GetField("languageCode", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).SetValue(__instance, LocalizedContentManager.LanguageCode.en);
+ }
+ }
+ [HarmonyPostfix]
+ public static void Postfix(StartupPreferences __instance)
+ {
+ if (ModEntry.ModConfig.CurrentLanguageCode > ModEntry.ModConfig.OriginLocaleCount)
+ {
+ typeof(StartupPreferences).GetField("languageCode", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).SetValue(__instance, ModEntry.ModConfig.CurrentLanguageCode);
+ }
+ }
+ }
+ [HarmonyPatch(typeof(StartupPreferences))]
+ [HarmonyPatch("readSettings")]
+ public class ReadSettingsRewrite
+ {
+ [HarmonyPostfix]
+ public static void Postfix(StartupPreferences __instance)
+ {
+ if (ModEntry.ModConfig.CurrentLanguageCode > ModEntry.ModConfig.OriginLocaleCount)
+ {
+ typeof(StartupPreferences).GetField("languageCode", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).SetValue(__instance, ModEntry.ModConfig.CurrentLanguageCode);
+ }
+ }
+ }
+ }
+}
diff --git a/Rewrites/TitleContainerRewrites.cs b/Rewrites/TitleContainerRewrites.cs
new file mode 100644
index 0000000..eb5b7dd
--- /dev/null
+++ b/Rewrites/TitleContainerRewrites.cs
@@ -0,0 +1,51 @@
+using System;
+using System.IO;
+using System.Reflection;
+using Harmony;
+using Microsoft.Xna.Framework;
+
+namespace StardewModdingAPI.Mods.CustomLocalization.Rewrites
+{
+ public class TitleContainerRewrites
+ {
+ [HarmonyPatch(typeof(TitleContainer))]
+ [HarmonyPatch("OpenStream")]
+ public class OpenStreamRewrite
+ {
+ [HarmonyPrefix]
+ public static bool Prefix(string name, ref Stream __result)
+ {
+ Stream stream = null;
+ if (string.IsNullOrEmpty(name))
+ {
+ return true;
+ }
+ string newPath = Path.Combine(ModEntry.ModPath, name);
+ string safeName = (string)typeof(TitleContainer).GetMethod("NormalizeRelativePath", BindingFlags.Static | BindingFlags.NonPublic).Invoke(null, new object[] { name });
+ try
+ {
+ stream = new FileStream(newPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
+ __result = stream;
+ return false;
+ }
+ catch
+ {
+ }
+ if (stream == null)
+ {
+ try
+ {
+ MethodInfo PlatformOpenStream = typeof(TitleContainer).GetMethod("PlatformOpenStream", BindingFlags.Static | BindingFlags.NonPublic);
+ stream = (Stream)PlatformOpenStream.Invoke(null, new object[] { safeName });
+ __result = stream;
+ }
+ catch
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+ }
+}
diff --git a/SMAPI.Mods.CustomLocalization.csproj b/SMAPI.Mods.CustomLocalization.csproj
new file mode 100644
index 0000000..2b54276
--- /dev/null
+++ b/SMAPI.Mods.CustomLocalization.csproj
@@ -0,0 +1,33 @@
+
+
+
+ CustomLocalization
+ StardewModdingAPI.Mods.CustomLocalization
+ net45
+ latest
+
+ false
+ x86
+
+
+
+
+ ..\libs\0Harmony.dll
+
+
+ ..\libs\\MonoGame.Framework.dll
+
+
+ ..\libs\StardewModdingAPI.dll
+
+
+ ..\libs\\StardewValley.dll
+
+
+
+
+
+ PreserveNewest
+
+
+
diff --git a/SMAPI.Mods.CustomLocalization.sln b/SMAPI.Mods.CustomLocalization.sln
new file mode 100644
index 0000000..d7740d5
--- /dev/null
+++ b/SMAPI.Mods.CustomLocalization.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29728.190
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SMAPI.Mods.CustomLocalization", "SMAPI.Mods.CustomLocalization.csproj", "{4E035E78-8288-415F-81D2-B4B3C489A005}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {4E035E78-8288-415F-81D2-B4B3C489A005}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4E035E78-8288-415F-81D2-B4B3C489A005}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4E035E78-8288-415F-81D2-B4B3C489A005}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4E035E78-8288-415F-81D2-B4B3C489A005}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {BD55E645-67A0-44E8-8478-BBF095C0F9D5}
+ EndGlobalSection
+EndGlobal
diff --git a/manifest.json b/manifest.json
new file mode 100644
index 0000000..ca15b14
--- /dev/null
+++ b/manifest.json
@@ -0,0 +1,9 @@
+{
+ "Name": "Custom Localization",
+ "Author": "ZaneYork",
+ "Version": "1.0.0",
+ "Description": "Localization for not exist locale.",
+ "UniqueID": "SMAPI.CustomLocalization",
+ "EntryDll": "CustomLocalization.dll",
+ "MinimumApiVersion": "3.2.0"
+}