private void Client_DownloadDataCompleted(object sender, DownloadDataCompletedEventArgs e) { if (e.Cancelled || e.Error != null) { this.DownloaderCompleted?.Invoke(sender, new DownloaderCompletedEventArgs(e.Cancelled, e.Error)); } else { var index = (int)e.UserState; SWFile swFile = SWFileManager.GetElementAt(index); if (swFile is ArchivedSWFile archivedSWFile) { archivedSWFile.Data = e.Result; } else { string swFilePath = Path.Combine(this.Language.Path, swFile.Path, Path.GetFileName(swFile.PathD)); string swFileDirectory = Path.GetDirectoryName(swFilePath); Directory.CreateDirectory(swFileDirectory); File.WriteAllBytes(swFilePath, e.Result); } if (SWFileManager.Count > ++index) { this.DownloadNext(index); } else { this.DownloaderCompleted?.Invoke(sender, new DownloaderCompletedEventArgs(this.Language, e.Cancelled, e.Error)); } } }
private void DownloadNext(int index) { string pathname = Language.Path.StartsWith("jpc") ? "jp" + Language.Path.Substring(3) : Language.Path; Uri uri = new Uri(Urls.TranslationGitHubHome + pathname + '/' + SWFileManager.GetElementAt(index).PathD); this.Client.DownloadDataAsync(uri, index); Logger.Debug(Methods.MethodFullName(System.Reflection.MethodBase.GetCurrentMethod(), uri.AbsoluteUri)); }
private void Worker_DoWork(object sender, DoWorkEventArgs e) { Logger.Debug(Methods.MethodFullName("Downloader", Thread.CurrentThread.ManagedThreadId.ToString(), this.Language.ToString())); if (UserSettings.BypassTranslationDateCheck || Methods.HasNewTranslations(this.Language) || Methods.IsTranslationOutdated(this.Language)) { SWFileManager.LoadFileConfiguration(this.Language); } else { throw new Exception(StringLoader.GetText("exception_already_latest_translation", Methods.DateToLocalString(this.Language.LastUpdate))); } }
private static void BackupAndPlaceFiles(Language language) { ReadOnlyCollection <SWFile> swFiles = SWFileManager.GetFiles(); ILookup <Type, SWFile> swFileTypeLookup = swFiles.ToLookup(f => f.GetType()); IEnumerable <string> archives = swFileTypeLookup[typeof(ArchivedSWFile)].Select(f => f.Path).Union(swFileTypeLookup[typeof(PatchedSWFile)].Select(f => f.Path)); IEnumerable <string> otherSWFiles = swFileTypeLookup[typeof(SWFile)].Select(f => f.Path + Path.GetFileName(f.PathD)); IEnumerable <string> translationFiles = archives.Distinct().Union(otherSWFiles); foreach (string path in translationFiles) { string originalFilePath = Path.Combine(UserSettings.GamePath, path); string translationFilePath = Path.Combine(language.Path, path); string backupFilePath = Path.Combine(language.BackupPath, path); BackupAndPlaceFile(originalFilePath, translationFilePath, backupFilePath); } }
private void Patcher_PatcherCompleted(object sender, PatcherCompletedEventArgs e) { if (e.Cancelled) { Logger.Debug($"{sender.ToString()} cancelled"); DeleteTmpFiles(e.Language); } else if (e.Error != null) { Logger.Error(e.Error); MsgBox.Error(Methods.ExeptionParser(e.Error)); DeleteTmpFiles(e.Language); } else { Logger.Debug($"{sender.ToString()} successfuly completed"); string clientIniPath = Path.Combine(UserSettings.GamePath, Strings.IniName.ClientVer); if (!Methods.LoadVerIni(out IniFile clientIni, clientIniPath)) { throw new Exception(StringLoader.GetText("exception_generic_read_error", clientIniPath)); } IniSection clientVerSection = clientIni.Sections[Strings.IniName.Ver.Section]; string translationIniPath = Path.Combine(e.Language.Path, Strings.IniName.Translation); var translationIni = new IniFile(); IniKey translationDateKey = new IniKey(translationIni, Strings.IniName.Patcher.KeyDate, Methods.DateToString(e.Language.LastUpdate)); IniSection translationPatcherSection = new IniSection(translationIni, Strings.IniName.Patcher.Section, translationDateKey); translationIni.Sections.Add(translationPatcherSection); translationIni.Sections.Add(clientVerSection.Copy(translationIni)); Logger.Debug($"Saving translation config to [{translationIniPath}]"); translationIni.Save(translationIniPath); } SWFileManager.DisposeFileData(); GC.Collect(); this.CurrentState = State.Idle; }
private static bool IsTranslationOutdatedOrMissing(Language language) { if (Methods.IsTranslationOutdated(language)) { return(true); } ReadOnlyCollection <SWFile> swFiles = SWFileManager.GetFiles(); ILookup <Type, SWFile> things = swFiles.ToLookup(f => f.GetType()); IEnumerable <string> archivesPaths = things[typeof(ArchivedSWFile)].Select(f => f.Path).Union(things[typeof(PatchedSWFile)].Select(f => f.Path)); IEnumerable <string> otherSWFilesPaths = things[typeof(SWFile)].Select(f => f.Path + Path.GetFileName(f.PathD)); IEnumerable <string> translationFilePaths = archivesPaths.Distinct().Union(otherSWFilesPaths).Select(f => Path.Combine(language.Path, f)); foreach (string path in translationFilePaths) { if (!File.Exists(path)) { return(true); } } return(false); }
private void Client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e) { this.DownloaderProgressChanged?.Invoke(sender, new DownloaderProgressChangedEventArgs((int)e.UserState + 1, SWFileManager.Count, Path.GetFileNameWithoutExtension(SWFileManager.GetElementAt((int)e.UserState).Name), e)); }
private void Worker_DoWork(object sender, DoWorkEventArgs e) { Logger.Debug(Methods.MethodFullName("Patcher", Thread.CurrentThread.ManagedThreadId.ToString(), this.Language.ToString())); this.CurrentState = State.Load; IEnumerable <ArchivedSWFile> archivedSWFiles = SWFileManager.GetFiles().OfType <ArchivedSWFile>(); string regionFldr = this.Language.ApplyingRegionFolder == "jpc" ? "jp" : this.Language.ApplyingRegionFolder; string datasArchivesPath = Urls.TranslationGitHubHome + regionFldr + '/' + Strings.IniName.DatasArchives; Logger.Debug(Methods.MethodFullName(System.Reflection.MethodBase.GetCurrentMethod(), datasArchivesPath)); Dictionary <string, string> passwordDictionary = LoadPasswords(datasArchivesPath); int archivedSWFilesCount = archivedSWFiles.Count(); var archives = archivedSWFiles.Select(f => f.Path).Distinct().ToDictionary(p => p, p => { string archivePath = Path.Combine(UserSettings.GamePath, p); Logger.Info($"Loading archive=[{archivePath}]"); byte[] fileBytes = File.ReadAllBytes(archivePath); var xms = new XorMemoryStream(fileBytes, SecretByte); return(ZipFile.Read(xms)); }); this.CurrentState = State.Patch; int count = 1; foreach (ArchivedSWFile archivedSWFile in archivedSWFiles) { if (this.Worker.CancellationPending) { e.Cancel = true; return; } this.Worker.ReportProgress(count++ == archivedSWFilesCount ? int.MaxValue : Convert.ToInt32(((double)count / archivedSWFilesCount) * int.MaxValue)); string archiveFileNameWithoutExtension = Path.GetFileNameWithoutExtension(archivedSWFile.Path); string archivePassword = null; if (passwordDictionary.ContainsKey(archiveFileNameWithoutExtension)) { archivePassword = passwordDictionary[archiveFileNameWithoutExtension]; } Logger.Info($"Patching file=[{archivedSWFile.PathA}] archive=[{archivedSWFile.Path}]"); if (archivedSWFile is PatchedSWFile patchedSWFile) { MemoryStream ms = Methods.GetZippedFileStream(archives[patchedSWFile.Path], patchedSWFile.PathA, archivePassword); MemoryStream msDest = new MemoryStream(); string[] fullFormatArray = patchedSWFile.Format.Split(' '); int idIndex = Convert.ToInt32(fullFormatArray[0]); string countFormat = fullFormatArray[1]; string[] formatArray = fullFormatArray.Skip(2).ToArray(); // skip idIndex and countFormat #region Patching the File ulong dataCount = 0; ulong dataSum = 0; ushort hashLength = 32; byte[] hash = new byte[hashLength]; int lineCount = 0; for (int i = 0; i < formatArray.Length; i++) { if (formatArray[i] == "len") { lineCount++; i++; } } Dictionary <ulong, string[]> inputTable = this.ReadInputFile(patchedSWFile.Data, lineCount, idIndex); using (var br = new BinaryReader(ms)) using (var bw = new BinaryWriter(msDest, new UTF8Encoding(false, true), true)) { switch (countFormat) { case "1": dataCount = br.ReadByte(); bw.Write(Convert.ToByte(dataCount)); break; case "2": dataCount = br.ReadUInt16(); bw.Write(Convert.ToUInt16(dataCount)); break; case "4": dataCount = br.ReadUInt32(); bw.Write(Convert.ToUInt32(dataCount)); break; case "8": dataCount = br.ReadUInt64(); bw.Write(Convert.ToUInt64(dataCount)); break; } ulong value = 0; for (ulong i = 0; i < dataCount; i++) { if (this.Worker.CancellationPending) { e.Cancel = true; break; } #region Object Reading object[] current = new object[formatArray.Length]; for (int j = 0; j < formatArray.Length; j++) { if (this.Worker.CancellationPending) { e.Cancel = true; break; } switch (formatArray[j]) { case "1": current[j] = Convert.ToByte(br.ReadByte()); break; case "2": current[j] = Convert.ToUInt16(br.ReadUInt16()); break; case "4": current[j] = Convert.ToUInt32(br.ReadUInt32()); break; case "8": current[j] = Convert.ToUInt64(br.ReadUInt64()); break; case "len": switch (formatArray[++j]) { case "1": value = br.ReadByte(); current[j] = Convert.ToByte(br.ReadByte()); break; case "2": value = br.ReadUInt16(); current[j] = Convert.ToUInt16(value); break; case "4": value = br.ReadUInt32(); current[j] = Convert.ToUInt32(value); break; case "8": value = br.ReadUInt64(); current[j] = Convert.ToUInt64(value); break; } ulong strBytesLength = value * 2; byte[] strBytes = new byte[strBytesLength]; current[j] = strBytes; for (ulong k = 0; k < strBytesLength; k++) { strBytes[k] = br.ReadByte(); } break; } } #endregion Object Reading #region Object Writing int lenPosition = 0; for (int j = 0; j < formatArray.Length; j++) { if (this.Worker.CancellationPending) { e.Cancel = true; break; } switch (formatArray[j]) { case "1": value = Convert.ToByte(current[j]); bw.Write(Convert.ToByte(value)); break; case "2": value = Convert.ToUInt16(current[j]); bw.Write(Convert.ToUInt16(value)); break; case "4": value = Convert.ToUInt32(current[j]); bw.Write(Convert.ToUInt32(value)); break; case "8": value = Convert.ToUInt64(current[j]); bw.Write(Convert.ToUInt64(value)); break; case "len": byte[] strBytes = null; j++; ulong id = Convert.ToUInt64(current[idIndex]); if (inputTable.ContainsKey(id)) { strBytes = Encoding.Unicode.GetBytes(inputTable[id][lenPosition++]); } else { strBytes = current[j] as byte[]; } value = Convert.ToUInt64(strBytes.Length / 2); switch (formatArray[j]) { case "1": bw.Write(Convert.ToByte(value)); break; case "2": bw.Write(Convert.ToUInt16(value)); break; case "4": bw.Write(Convert.ToUInt32(value)); break; case "8": bw.Write(Convert.ToUInt64(value)); break; } foreach (byte b in strBytes) { dataSum += b; bw.Write(b); } break; } dataSum += value; } #endregion Object Writing } bw.Write(hashLength); string hashString = GetMD5(Convert.ToString(dataSum)); for (int i = 0; i < hashLength; i++) { hash[i] = Convert.ToByte(hashString[i]); } bw.Write(hash); } #endregion Patching the File Methods.ZipFileStream(archives[patchedSWFile.Path], patchedSWFile.PathA, msDest, archivePassword); } else { var ms = new MemoryStream(archivedSWFile.Data); if (Path.GetExtension(archivedSWFile.PathD) == ".zip") { Methods.AddZipToZip(archives[archivedSWFile.Path], archivedSWFile.PathA, ms, archivePassword); } else { Methods.ZipFileStream(archives[archivedSWFile.Path], archivedSWFile.PathA, ms, archivePassword); } } } this.CurrentState = State.Save; foreach (KeyValuePair <string, ZipFile> archive in archives) { if (this.Worker.CancellationPending) { e.Cancel = true; return; } string zipFileName = archive.Key; ZipFile zipFile = archive.Value; string archivePath = Path.Combine(this.Language.Path, zipFileName); string archivePathDirectory = Path.GetDirectoryName(archivePath); Directory.CreateDirectory(archivePathDirectory); //Dirty af but will prevent "Not a valid Win32 FileTime" error foreach (ZipEntry ze in zipFile.Entries) { ze.ModifiedTime = DateTime.Now; } using (var fs = new MemoryStream()) { zipFile.Save(fs); byte[] buffer = fs.ToArray(); zipFile.Dispose(); // TODO: using () { } for (int i = 0; i < buffer.Length; i++) { buffer[i] ^= SecretByte; } File.WriteAllBytes(archivePath, buffer); } } /* * Disabled for now since it's useless * * if (UserSettings.WantToPatchExe) * { * this.CurrentState = State.ExePatch; * * string regionId = this.Language.ApplyingRegionId == "jpc" ? "jp" : this.Language.ApplyingRegionId; * string regionFolder = this.Language.ApplyingRegionFolder == "jpc" ? "jp" : this.Language.ApplyingRegionFolder; * string gameExePath = Path.Combine(UserSettings.GamePath, Methods.GetGameExeName(regionId)); * byte[] gameExeBytes = File.ReadAllBytes(gameExePath); * string gameExePatchedPath = Path.Combine(UserSettings.PatcherPath, regionFolder, Methods.GetGameExeName(regionId)); * * Methods.PatchExeFile(gameExeBytes, gameExePatchedPath, Urls.TranslationGitHubHome + regionFolder + '/' + Strings.IniName.BytesToPatch); * } */ }
private void Worker_DoWork(object sender, DoWorkEventArgs e) { this.Worker.ReportProgress((int)State.Prepare); if (this.PlaceTranslations) { Logger.Debug(Methods.MethodFullName("GameStart", Thread.CurrentThread.ManagedThreadId.ToString(), this.Language.ApplyingRegionId, this.Language.ToString())); SWFileManager.LoadFileConfiguration(this.Language); if (IsTranslationOutdatedOrMissing(this.Language)) { e.Result = true; // call force patch in completed event return; } if (UserSettings.WantToPatchExe) { string regionId = this.Language.ApplyingRegionId == "jpc" ? "jp" : this.Language.ApplyingRegionId; string regionFolder = this.Language.ApplyingRegionFolder == "jpc" ? "jp" : this.Language.ApplyingRegionFolder; string gameExePath = Path.Combine(UserSettings.GamePath, Methods.GetGameExeName(regionId)); string gameExePatchedPath = Path.Combine(UserSettings.PatcherPath, regionId, Methods.GetGameExeName(regionId)); string backupFilePath = Path.Combine(this.Language.BackupPath, Methods.GetGameExeName(regionId)); if (!File.Exists(gameExePatchedPath)) { byte[] gameExeBytes = File.ReadAllBytes(gameExePath); Methods.PatchExeFile(gameExeBytes, gameExePatchedPath, Urls.TranslationGitHubHome + regionFolder + '/' + Strings.IniName.BytesToPatch); } BackupAndPlaceFile(gameExePath, gameExePatchedPath, backupFilePath); } Process clientProcess = null; if (UserSettings.WantToLogin) { string regionId = this.Language.ApplyingRegionId; switch (regionId) { case "jp": StartHangeJP(() => BackupAndPlaceFiles(this.Language)); this.Worker.ReportProgress((int)State.WaitClient); while (true) { if (this.Worker.CancellationPending) { e.Cancel = true; return; } clientProcess = GetProcess(Methods.GetGameExeName(regionId)); if (clientProcess == null) { Thread.Sleep(1000); } else { break; } } break; case "gjp": StartGamecomJP(() => BackupAndPlaceFiles(this.Language)); this.Worker.ReportProgress((int)State.WaitClient); while (true) { if (this.Worker.CancellationPending) { e.Cancel = true; return; } clientProcess = GetProcess(Methods.GetGameExeName(regionId)); if (clientProcess == null) { Thread.Sleep(1000); } else { break; } } break; case "kr": StartStoveKR(() => BackupAndPlaceFiles(this.Language)); this.Worker.ReportProgress((int)State.WaitClient); while (true) { if (this.Worker.CancellationPending) { e.Cancel = true; return; } clientProcess = GetProcess(Methods.GetGameExeName(regionId)); if (clientProcess == null) { Thread.Sleep(1000); } else { break; } } break; case "nkr": Methods.RegionDoesNotSupportLogin(); break; case "jpc": case "gf": Methods.RegionDoesNotSupportLogin(); break; default: throw new Exception(StringLoader.GetText("exception_region_unknown", regionId)); } } else if (Language.ApplyingRegionId == "jpc") { BackupAndPlaceFiles(this.Language); clientProcess = StartCustomGame(); } else { BackupAndPlaceFiles(this.Language); this.Worker.ReportProgress((int)State.WaitClient); while (true) { if (this.Worker.CancellationPending) { e.Cancel = true; return; } clientProcess = GetProcess(Methods.GetGameExeName(this.Language.ApplyingRegionId)); if (clientProcess == null) { Thread.Sleep(1000); } else { break; } } } this.Worker.ReportProgress((int)State.WaitClose); clientProcess.WaitForExit(); } else { Logger.Debug(Methods.MethodFullName("GameStart", Thread.CurrentThread.ManagedThreadId.ToString(), this.Language.ApplyingRegionId)); if (UserSettings.WantToLogin) { string regionId = this.Language.ApplyingRegionId; switch (regionId) { case "jp": StartHangeJP(); e.Cancel = true; break; case "gjp": StartGamecomJP(); e.Cancel = true; break; case "jpc": StartCustomGame(); e.Cancel = true; break; case "kr": StartStoveKR(); e.Cancel = true; break; case "nkr": Methods.RegionDoesNotSupportLogin(); break; case "gf": Methods.RegionDoesNotSupportLogin(); break; default: throw new Exception(StringLoader.GetText("exception_region_unknown", regionId)); } } else { throw new Exception(StringLoader.GetText("exception_not_login_option")); } } }
private void Worker_DoWork(object sender, DoWorkEventArgs e) { this.Worker.ReportProgress((int)State.Prepare); if (this.PlaceTranslations) { Logger.Debug(Methods.MethodFullName("GameStart", Thread.CurrentThread.ManagedThreadId.ToString(), this.Language.ApplyingRegionId, this.Language.ToString())); SWFileManager.LoadFileConfiguration(this.Language); if (IsTranslationOutdatedOrMissing(this.Language)) { e.Result = true; // call force patch in completed event return; } if (UserSettings.WantToPatchExe) { string regionId = this.Language.ApplyingRegionId; string gameExePath = Path.Combine(UserSettings.GamePath, Methods.GetGameExeName(regionId)); string gameExePatchedPath = Path.Combine(UserSettings.PatcherPath, regionId, Methods.GetGameExeName(regionId)); string backupFilePath = Path.Combine(this.Language.BackupPath, Methods.GetGameExeName(regionId)); if (!File.Exists(gameExePatchedPath)) { byte[] gameExeBytes = File.ReadAllBytes(gameExePath); Methods.PatchExeFile(gameExeBytes, gameExePatchedPath, Urls.TranslationGitHubHome + regionId + '/' + Strings.IniName.BytesToPatch); } BackupAndPlaceFile(gameExePath, gameExePatchedPath, backupFilePath); } Process clientProcess = null; ProcessStartInfo startInfo = null; if (UserSettings.WantToLogin) { string regionId = this.Language.ApplyingRegionId; switch (regionId) { case "jp": Methods.RegionDoesNotSupportLogin(); // TODO: jp login? break; using (var client = new MyWebClient()) { HangameLogin(client); string[] gameStartArgs = GetGameStartArguments(client); startInfo = new ProcessStartInfo { UseShellExecute = true, Verb = "runas", Arguments = String.Join(" ", gameStartArgs.Select(s => "\"" + s + "\"")), WorkingDirectory = UserSettings.GamePath, FileName = Methods.GetGameExeName(regionId) }; } BackupAndPlaceFiles(this.Language); clientProcess = Process.Start(startInfo); break; case "kr": LoginStartKR(); this.Worker.ReportProgress((int)State.WaitClient); while (true) { if (this.Worker.CancellationPending) { e.Cancel = true; return; } clientProcess = GetProcess(Methods.GetGameExeName(regionId)); if (clientProcess == null) { Thread.Sleep(1000); } else { break; } } break; case "nkr": Methods.RegionDoesNotSupportLogin(); break; } } else { BackupAndPlaceFiles(this.Language); this.Worker.ReportProgress((int)State.WaitClient); while (true) { if (this.Worker.CancellationPending) { e.Cancel = true; return; } clientProcess = GetProcess(Methods.GetGameExeName(this.Language.ApplyingRegionId)); if (clientProcess == null) { Thread.Sleep(1000); } else { break; } } } this.Worker.ReportProgress((int)State.WaitClose); clientProcess.WaitForExit(); } else { Logger.Debug(Methods.MethodFullName("GameStart", Thread.CurrentThread.ManagedThreadId.ToString(), this.Language.ApplyingRegionId)); if (UserSettings.WantToLogin) { switch (this.Language.ApplyingRegionId) { case "jp": Methods.RegionDoesNotSupportLogin(); // TODO: jp login? break; StartRawJP(); e.Cancel = true; break; case "kr": StartRawKR(); e.Cancel = true; break; case "nkr": Methods.RegionDoesNotSupportLogin(); break; } } else { throw new Exception(StringLoader.GetText("exception_not_login_option")); } } }
private void DownloadNext(int index) { Uri uri = new Uri(Urls.TranslationGitHubHome + this.Language.Path + '/' + SWFileManager.GetElementAt(index).PathD); this.Client.DownloadDataAsync(uri, index); Logger.Debug(Methods.MethodFullName(System.Reflection.MethodBase.GetCurrentMethod(), uri.AbsoluteUri)); }