├── Editor ├── BuildVPK.cs └── PSVitaUtilitesSettings.cs ├── README.md └── mcs.rsp /Editor/BuildVPK.cs: -------------------------------------------------------------------------------- 1 | using UnityEditor; 2 | using UnityEngine; 3 | using System.IO; 4 | using System.IO.Compression; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Net; 8 | using System.Net.Sockets; 9 | using System.Threading.Tasks; 10 | using PSVitaUtilities.Settings; 11 | using System.Security.Cryptography; 12 | 13 | namespace PSVitaUtilities.Building 14 | { 15 | public class BuildVPK : EditorWindow 16 | { 17 | static string buildType = ""; 18 | static string buildCache = Application.dataPath.TrimEnd("Assets".ToCharArray()) + "/Build/BuildCache"; 19 | 20 | #region Build Functions 21 | [MenuItem("PSVita/Build/Build VPK", false, -1)] 22 | public static void BuildGameNormal() => BuildGame(BuildMode.Normal); 23 | 24 | [MenuItem("PSVita/Build/FTP/Build and Send VPK")] 25 | public static void BuildGameFTP() => BuildGame(BuildMode.FTP); 26 | 27 | [MenuItem("PSVita/Build/FTP/Transfer Build")] 28 | public static void BuildFTPTransfer() => BuildGame(BuildMode.FTPTransfer); 29 | 30 | [MenuItem("PSVita/Build/FTP/Build and Run (WIP)")] 31 | public static void BuildGameRun() => BuildGame(BuildMode.Run); 32 | 33 | [MenuItem("PSVita/Build/USB/Build and Send VPK (WIP)")] 34 | public static void BuildGameUSB() => BuildGame(BuildMode.USB); 35 | 36 | //[MenuItem("PSVita/Build/USB/Build and Run (WIP)")] 37 | public static void BuildGameUSBRun() => BuildGame(BuildMode.USBRun); 38 | 39 | //[MenuItem("PSVita/Build/Emulator/Build and Transfer")] 40 | public static void BuildEmuTransfer() => BuildGame(BuildMode.EmuTransfer); 41 | 42 | public static void BuildGame(BuildMode buildMode) 43 | { 44 | DateTime startTime = DateTime.Now; 45 | 46 | string Error = "Project Failed To Build"; 47 | string filePath = ""; 48 | 49 | #region Set Build Type for Error Messages 50 | switch (buildMode) 51 | { 52 | case BuildMode.Normal: 53 | buildType = "VPK Build"; 54 | break; 55 | case BuildMode.FTP: 56 | buildType = "FTP VPK Build"; 57 | break; 58 | case BuildMode.Run: 59 | buildType = "Build and Run"; 60 | break; 61 | case BuildMode.USB: 62 | buildType = "USB VPK Build"; 63 | break; 64 | case BuildMode.USBRun: 65 | buildType = "USB Build and Run Build"; 66 | break; 67 | case BuildMode.EmuTransfer: 68 | buildType = "Emulator Transfer"; 69 | break; 70 | case BuildMode.FTPTransfer: 71 | buildType = "FTP Build Transfer"; 72 | break; 73 | } 74 | #endregion 75 | 76 | try 77 | { 78 | #region Load Settings 79 | Error = $"{buildType} Failed (Failed to Load Settings)"; 80 | PSVitaUtilitiesSettings.LoadSettings(); 81 | #endregion 82 | 83 | #region Build and Run check 84 | if (buildMode == BuildMode.Run) 85 | { 86 | if (!EditorUtility.DisplayDialog("Are you sure?", "This is a work in progress function that could potentally crash your vita and crash in Unity 2017. It requires the game being preinstalled on the system at least once", "Continue", "Stop")) 87 | return; 88 | 89 | EditorUtility.DisplayProgressBar("Building", "Killing All Apps On Vita...", 0f / 6f); 90 | Error = $"{buildType} Failed (Network Error: Failed to kill all apps)"; 91 | VitaDestroy(); 92 | } 93 | if (buildMode == BuildMode.USBRun) 94 | { 95 | if (!EditorUtility.DisplayDialog("Are you sure?", "This is a work in progress function that could potentally crash your vita and crash in Unity 2017. It requires the game being preinstalled on the system at least once", "Continue", "Stop")) 96 | return; 97 | 98 | ScreenOn(); 99 | //EditorUtility.DisplayProgressBar("Building", "Killing All Apps On Vita...", 0f / 6f); 100 | //Error = $"{buildType} Failed (Network Error: Failed to kill all apps)"; 101 | //VitaDestroy(); 102 | //VitaShellLaunch(); 103 | //if (!EditorUtility.DisplayDialog("Have you enabled USB transfer on VitaShell?", "This function requires that the VitaShell USB transfer is enabled and active. Please press continue if you have done so.", "Continue", "Stop")) 104 | // return; 105 | } 106 | #endregion 107 | 108 | #region TitleID Check 109 | Error = $"{buildType} Failed (TitleID Check Error: Try turning off the TitleID Check in settings or Refreshing the VitaDB in settings)"; 110 | if (!PSVitaUtilitiesSettings.CheckValidTitleID()) 111 | { 112 | Debug.LogError("TitleID Error. TitleID can be changed in PlayerSettings or in the PSVita Settings"); 113 | return; 114 | } 115 | #endregion 116 | 117 | #region Prepare Filepaths 118 | EditorUtility.DisplayProgressBar("Building", "Starting process...", 1f / 5f); 119 | Error = $"{buildType} Failed (Failed to set path)"; 120 | string BuildPath = Application.dataPath.TrimEnd("Assets".ToCharArray()) + "/Build/TempBuild"; 121 | if (buildMode == BuildMode.Normal) 122 | { 123 | filePath = EditorUtility.SaveFilePanel("Save VPK file", "", "", "vpk"); 124 | if (filePath == "") 125 | { 126 | EditorUtility.ClearProgressBar(); 127 | return; 128 | } 129 | } 130 | else 131 | { 132 | filePath = Application.dataPath.TrimEnd("Assets".ToCharArray()) + "/Build/" + PlayerSettings.productName + ".vpk"; 133 | } 134 | #endregion 135 | 136 | #region Create Build Directory 137 | Error = $"{buildType} Failed (Failed to Create Directory)"; 138 | Directory.CreateDirectory(BuildPath); 139 | #endregion 140 | 141 | #region Build Project 142 | Error = $"{buildType} Failed (Failed to Build)"; 143 | if (buildMode != BuildMode.Run && buildMode != BuildMode.FTPTransfer) 144 | { 145 | if (Directory.Exists(buildCache)) 146 | Directory.Delete(buildCache, true); 147 | } 148 | EditorUtility.DisplayProgressBar("Building", "Building project...", 2f / 5f); 149 | BuildPipeline.BuildPlayer(EditorBuildSettings.scenes, BuildPath, BuildTarget.PSP2, BuildOptions.None); 150 | #endregion 151 | 152 | #region Delete Junk 153 | EditorUtility.DisplayProgressBar("Building", "Deleting junk...", 3f / 5f); 154 | Error = $"{buildType} Failed (Failed to Delete Junk)"; 155 | DeleteJunk(BuildPath); 156 | #endregion 157 | 158 | #region Remove Trial 159 | Error = $"{buildType} Failed (Failed to edit TempBuild.self)"; 160 | EditorUtility.DisplayProgressBar("Building", "Removing trial...", 4f / 5f); 161 | RemoveTrial(BuildPath); 162 | #endregion 163 | 164 | #region Make VPK 165 | if (buildMode != BuildMode.Run && buildMode != BuildMode.FTPTransfer && buildMode != BuildMode.EmuTransfer) 166 | { 167 | Error = $"{buildType} Failed (Failed to zip To VPK)"; 168 | EditorUtility.DisplayProgressBar("Building", "Finalising build...", 5f / 6f); 169 | 170 | MakeZip(BuildPath, filePath); 171 | } 172 | #endregion 173 | 174 | DateTime startTransferTime = DateTime.Now; 175 | 176 | #region FTP - Send to Vita 177 | if (buildMode == BuildMode.FTP) 178 | { 179 | Error = $"{buildType} Failed (Network Error: Failed to transfer Build)"; 180 | string productName = PlayerSettings.productName; 181 | EditorUtility.DisplayProgressBar("Building", "Transfering " + productName + ".vpk", 6f / 7f); 182 | TransferFTPVPK(filePath, "ftp://" + PSVitaUtilitiesSettings.PSVitaIP + ":1337/" + PSVitaUtilitiesSettings.FTPLocation + "/" + productName + ".vpk"); 183 | } 184 | #endregion 185 | 186 | #region USB - Send to Vita 187 | if (buildMode == BuildMode.USB) 188 | { 189 | Error = $"{buildType} Failed (Transfer Error: Failed to transfer Build)"; 190 | string productName = PlayerSettings.productName; 191 | EditorUtility.DisplayProgressBar("Building", "Transfering " + productName + ".vpk", 6f / 7f); 192 | TransferUSBVPK(filePath, $"{PSVitaUtilitiesSettings.USBDriveLetter}/{productName}.vpk"); 193 | } 194 | #endregion 195 | 196 | #region FTP - Build and Run - Send to Vita and Run 197 | if (buildMode == BuildMode.Run || buildMode == BuildMode.FTPTransfer) 198 | { 199 | #region Send to Vita 200 | Error = $"{buildType} Failed (Network Error: Failed to Transfer Build)"; 201 | EditorUtility.DisplayProgressBar("Building", "Transfering build...", 5f / 6f); 202 | string temp = PSVitaUtilitiesSettings.TitleID; 203 | TransferFolder(BuildPath, "ftp://" + PSVitaUtilitiesSettings.PSVitaIP + ":1337/ux0:/app/" + temp); 204 | #endregion 205 | if (buildMode == BuildMode.Run) 206 | { 207 | #region Reboot Vita 208 | EditorUtility.DisplayProgressBar("Building", "Booting Game...", 6f / 7f); 209 | Error = $"{buildType} Failed (Network Error: Failed to reboot Vita)"; 210 | #endregion 211 | } 212 | } 213 | #endregion 214 | 215 | #region USB - Build and Run - Send to Vita and Run 216 | if (buildMode == BuildMode.USBRun) 217 | { 218 | #region Send to Vita 219 | Error = $"{buildType} Failed (Network Error: Failed to Transfer Build)"; 220 | EditorUtility.DisplayProgressBar("Building", "Transfering build...", 5f / 6f); 221 | string temp = PSVitaUtilitiesSettings.TitleID; 222 | TransferFolderUSB(BuildPath, $"{PSVitaUtilitiesSettings.USBDriveLetter}app/{temp}"); 223 | #endregion 224 | } 225 | #endregion 226 | 227 | #region Emu - Transfer to Emu 228 | if (buildMode == BuildMode.EmuTransfer) 229 | { 230 | Error = $"{buildType} Failed (Failed to transfer Build)"; 231 | EditorUtility.DisplayProgressBar("Building", "Transfering build...", 6f / 6f); 232 | CopyDirectory(BuildPath, PSVitaUtilitiesSettings.EmuStoragePath + "\\ux0/app\\" + PSVitaUtilitiesSettings.TitleID, true); 233 | } 234 | #endregion 235 | 236 | TimeSpan transferTime = DateTime.Now - startTransferTime; 237 | Debug.Log($"{buildType} Transfer complete in {transferTime:mm':'ss}!"); 238 | TimeSpan buildTime = DateTime.Now - startTime; 239 | Debug.Log($"{buildType} Complete in {buildTime:mm':'ss}!"); 240 | 241 | #region Run Game 242 | if (buildMode == BuildMode.Run || buildMode == BuildMode.USBRun) 243 | { 244 | if (PSVitaUtilitiesSettings.BuildRunReboot) 245 | { 246 | VitaReboot(); 247 | if (EditorUtility.DisplayDialog("Start Game?", "Is the Vita ready to start the game?", "Yes", "No")) 248 | Error = $"{buildType} Failed (Network Error: Failed To Launch Game. Was the Vita fully rebooted?)"; 249 | } 250 | 251 | ScreenOn(); 252 | LaunchGame(); 253 | 254 | EditorUtility.ClearProgressBar(); 255 | } 256 | #endregion 257 | } 258 | catch 259 | { 260 | Debug.LogError(Error); 261 | } 262 | EditorUtility.ClearProgressBar(); 263 | } 264 | #endregion 265 | 266 | #region Vita Control Functions 267 | [MenuItem("PSVita/Vita Control/Launch Game")] 268 | public static void LaunchGame() 269 | { 270 | PSVitaUtilitiesSettings.LoadSettings(); 271 | if (PSVitaUtilitiesSettings.KillAllAppsBeforeLaunch) 272 | VitaDestroy(); 273 | 274 | VitaControl($"launch { PSVitaUtilitiesSettings.TitleID}"); 275 | } 276 | 277 | [MenuItem("PSVita/Vita Control/Launch Shell")] 278 | public static void VitaShellLaunch() 279 | { 280 | PSVitaUtilitiesSettings.LoadSettings(); 281 | VitaControl("launch VITASHELL"); 282 | } 283 | 284 | [MenuItem("PSVita/Vita Control/Screen On")] 285 | public static void ScreenOn() => VitaControl("screen on"); 286 | 287 | [MenuItem("PSVita/Vita Control/Screen Off")] 288 | public static void ScreenOff() => VitaControl("screen off"); 289 | 290 | [MenuItem("PSVita/Vita Control/Reboot")] 291 | public static void VitaReboot() => VitaControl("reboot"); 292 | 293 | [MenuItem("PSVita/Vita Control/Kill All")] 294 | public static void VitaDestroy() => VitaControl("destroy"); 295 | 296 | static void VitaControl(string action) 297 | { 298 | PSVitaUtilitiesSettings.LoadSettings(); 299 | TcpClient client = new TcpClient(PSVitaUtilitiesSettings.PSVitaIP, (Int32)1338); 300 | 301 | Byte[] data = System.Text.Encoding.ASCII.GetBytes($"{action}\n"); 302 | NetworkStream stream = client.GetStream(); 303 | stream.Write(data, 0, data.Length); 304 | stream.Close(); 305 | client.Close(); 306 | } 307 | #endregion 308 | 309 | #region Helper Functions 310 | public static long FindPosition(Stream stream, byte[] byteSequence) 311 | { 312 | int b; 313 | long i = 0; 314 | while ((b = stream.ReadByte()) != -1) 315 | { 316 | if (b == byteSequence[i++]) 317 | { 318 | if (i == byteSequence.Length) 319 | return stream.Position - byteSequence.Length; 320 | } 321 | else 322 | i = b == byteSequence[0] ? 1 : 0; 323 | } 324 | 325 | return -1; 326 | } 327 | private static bool Skippable(string file) 328 | { 329 | string[] skippableEntries = new string[] { "sce_sys", "sce_module", "eboot.bin" }; 330 | for (int i = 0; i < skippableEntries.Length; i++) 331 | { 332 | if (file.Contains(skippableEntries[i])) 333 | return true; 334 | } 335 | return false; 336 | } 337 | private static string[] BuildRunCache(string _filePath) 338 | { 339 | var New = Directory.GetFiles(_filePath, "*.*", SearchOption.AllDirectories); 340 | 341 | if (!Directory.Exists(buildCache) || !PSVitaUtilitiesSettings.SizeCheck) 342 | return New; 343 | 344 | var Old = Directory.GetFiles(buildCache, "*.*", SearchOption.AllDirectories); 345 | List files = new List(); 346 | for (int i = 0; i < New.Length; i++) 347 | { 348 | bool samePathLength = false; 349 | for (int a = 0; a < Old.Length; a++) 350 | { 351 | if (New[i].Substring(_filePath.Length) == Old[a].Substring(buildCache.Length)) 352 | { 353 | samePathLength = true; 354 | FileInfo newFile = new FileInfo(New[i]); 355 | FileInfo oldFile = new FileInfo(Old[a]); 356 | if (newFile.Length != oldFile.Length) 357 | files.Add(New[i]); 358 | else 359 | { 360 | if (!FilesAreEqual_Hash(newFile, oldFile)) 361 | files.Add(New[i]); 362 | } 363 | } 364 | } 365 | if (!samePathLength) 366 | files.Add(New[i]); 367 | } 368 | bool oldFileMissing = false; 369 | for (int a = 0; a < Old.Length; a++) 370 | { 371 | for (int i = 0; i < New.Length; i++) 372 | { 373 | oldFileMissing = true; 374 | if (New[i].Substring(_filePath.Length) == Old[a].Substring(buildCache.Length)) 375 | { 376 | oldFileMissing = false; 377 | break; 378 | } 379 | } 380 | if (oldFileMissing) 381 | { 382 | files = new List(); 383 | Debug.LogError("Failed file found in cache that isnt on vita"); 384 | break; 385 | } 386 | } 387 | if (files.Count == 0) 388 | { 389 | if (!EditorUtility.DisplayDialog("Continue?", "All files appear to be exactly the same do you want to continue? (File difference check can be disabled in settings)", "Yes", "No")) 390 | Debug.LogError("Files the same size, not transfering anything"); 391 | else 392 | return New; 393 | } 394 | return files.ToArray(); 395 | } 396 | static bool FilesAreEqual_Hash(FileInfo first, FileInfo second) 397 | { 398 | Stream stream = first.OpenRead(); 399 | Stream stream1 = second.OpenRead(); 400 | byte[] firstHash = MD5.Create().ComputeHash(stream); 401 | byte[] secondHash = MD5.Create().ComputeHash(stream1); 402 | stream.Close(); 403 | stream1.Close(); 404 | for (int i = 0; i < firstHash.Length; i++) 405 | { 406 | if (firstHash[i] != secondHash[i]) 407 | return false; 408 | } 409 | return true; 410 | } 411 | static void CopyDirectory(string sourceDir, string destinationDir, bool recursive) 412 | { 413 | // Get information about the source directory 414 | var dir = new DirectoryInfo(sourceDir); 415 | 416 | // Check if the source directory exists 417 | if (!dir.Exists) 418 | throw new DirectoryNotFoundException($"Source directory not found: {dir.FullName}"); 419 | 420 | // Cache directories before we start copying 421 | DirectoryInfo[] dirs = dir.GetDirectories(); 422 | 423 | // Create the destination directory 424 | Directory.CreateDirectory(destinationDir); 425 | 426 | // Get the files in the source directory and copy to the destination directory 427 | foreach (FileInfo file in dir.GetFiles()) 428 | { 429 | string targetFilePath = Path.Combine(destinationDir, file.Name); 430 | file.CopyTo(targetFilePath); 431 | } 432 | 433 | // If recursive and copying subdirectories, recursively call this method 434 | if (recursive) 435 | { 436 | foreach (DirectoryInfo subDir in dirs) 437 | { 438 | string newDestinationDir = Path.Combine(destinationDir, subDir.Name); 439 | CopyDirectory(subDir.FullName, newDestinationDir, true); 440 | } 441 | } 442 | } 443 | #endregion 444 | 445 | #region Build Stages 446 | private static void DeleteJunk(string _buildPath) 447 | { 448 | Directory.Delete(_buildPath + "/SymbolFiles", true); 449 | File.Delete(_buildPath + "/configuration.psp2path"); 450 | File.Delete(_buildPath + "/TempBuild.bat"); 451 | } 452 | private static void RemoveTrial(string _buildPath) 453 | { 454 | using (Stream stream = File.Open(_buildPath + "/TempBuild.self", FileMode.Open)) 455 | { 456 | stream.Position = 0x80; 457 | stream.WriteByte(0x00); 458 | stream.Position = 0x00; 459 | if (!PSVitaUtilitiesSettings.ShowTrialWatermark) 460 | { 461 | long pos = FindPosition(stream, new byte[] { 0x74, 0x72, 0x69, 0x61, 0x6C, 0x2E, 0x70, 0x6E, 0x67 }); 462 | stream.Position = pos; 463 | stream.WriteByte(0x00); 464 | } 465 | } 466 | System.IO.File.Move(_buildPath + "/TempBuild.self", _buildPath + "/eboot.bin"); 467 | } 468 | private static void MakeZip(string _buildPath, string _filePath) 469 | { 470 | if (File.Exists(_filePath)) 471 | File.Delete(_filePath); 472 | 473 | ZipFile.CreateFromDirectory(_buildPath, _filePath); 474 | } 475 | 476 | private static void TransferFTPVPK(string _filePath, string _ip) 477 | { 478 | WebClient client = new WebClient(); 479 | int error = 0; 480 | bool done = false; 481 | while (!done) 482 | { 483 | try 484 | { 485 | client.UploadFile(new Uri(_ip), _filePath); 486 | done = true; 487 | } 488 | catch 489 | { 490 | error++; 491 | if (error >= PSVitaUtilitiesSettings.RetryCount) 492 | { 493 | done = true; 494 | Debug.LogError($"{buildType} Failed (Network Error: Failed to transfer Build)"); 495 | } 496 | } 497 | } 498 | } 499 | private static void TransferFolder(string _filePath, string _ip) 500 | { 501 | WebClient client = new WebClient(); 502 | 503 | var temp = BuildRunCache(_filePath); 504 | if (temp == null) return; 505 | 506 | List error = new List(); 507 | int retry = 0; 508 | for (int i = 0; i < temp.Length; i++) 509 | { 510 | if (!Skippable(temp[i])) 511 | { 512 | string fileBeingTransferred = temp[i].Substring(_filePath.Length); 513 | try 514 | { 515 | if (retry >= PSVitaUtilitiesSettings.RetryCount) break; 516 | 517 | EditorUtility.DisplayProgressBar("Building", $"Transfering {fileBeingTransferred}...", (float)i / (float)temp.Length); 518 | Task.Delay(1000); 519 | client.UploadFile(new Uri(_ip + fileBeingTransferred), temp[i]); 520 | retry = 0; 521 | } 522 | catch 523 | { 524 | retry++; 525 | Debug.LogError("Network Error on " + fileBeingTransferred); 526 | error.Add(i); 527 | } 528 | } 529 | } 530 | // Retry transfer on failed files 531 | if (error.Count != 0 && retry < 3) 532 | { 533 | while (error.Count != 0) 534 | { 535 | for (int i = 0; i < error.Count; i++) 536 | { 537 | try 538 | { 539 | if (error.Count != 0) 540 | { 541 | client.UploadFile(new Uri(_ip + temp[error[i]].Substring(_filePath.Length)), temp[error[i]]); 542 | error.RemoveAt(i); 543 | i--; 544 | retry = 0; 545 | } 546 | } 547 | catch 548 | { 549 | retry++; 550 | if (retry >= PSVitaUtilitiesSettings.RetryCount) 551 | { 552 | Debug.LogError($"{buildType} Failed (Network Error: Failed to Transfer Build, Retry Count exceeded.)"); 553 | error.RemoveRange(0, error.Count); 554 | } 555 | } 556 | } 557 | } 558 | } 559 | else if (retry >= PSVitaUtilitiesSettings.RetryCount) 560 | { 561 | Debug.LogError($"{buildType} Failed (Network Error: Failed to Transfer Build, Retry Count exceeded.)"); 562 | } 563 | 564 | if (Directory.Exists(buildCache)) 565 | Directory.Delete(buildCache, true); 566 | 567 | CopyDirectory(_filePath, buildCache, true); 568 | } 569 | 570 | private static void TransferUSBVPK(string _source, string _dest) 571 | { 572 | try { File.Copy(_source, _dest, true); } 573 | catch { Debug.LogError($"{buildType} Failed (Transfer Error: Failed to transfer Build)"); } 574 | } 575 | 576 | private static void TransferFolderUSB(string _source, string _dest) 577 | { 578 | try 579 | { 580 | DirectoryInfo target = new DirectoryInfo(_dest); 581 | DirectoryInfo source = new DirectoryInfo(_source); 582 | 583 | if (!Directory.Exists(target.FullName)) 584 | { 585 | Debug.LogError("Please ensure you have installed the project already before using Build and Run"); 586 | throw new Exception(); 587 | } 588 | 589 | // Copy each file into it’s new directory. 590 | foreach (FileInfo fi in source.GetFiles()) 591 | { 592 | if (!Skippable(fi.Name)) 593 | { 594 | //Debug.Log($"Copying {target.FullName}\\{fi.Name}"); 595 | fi.CopyTo(Path.Combine(target.ToString(), fi.Name), true); 596 | } 597 | } 598 | 599 | // Copy each subdirectory using recursion. 600 | foreach (DirectoryInfo diSourceSubDir in source.GetDirectories()) 601 | { 602 | if (!Skippable(diSourceSubDir.Name)) 603 | { 604 | DirectoryInfo nextTargetSubDir = 605 | target.CreateSubdirectory(diSourceSubDir.Name); 606 | TransferFolderUSB(diSourceSubDir.ToString(), nextTargetSubDir.ToString()); 607 | } 608 | } 609 | } 610 | catch 611 | { 612 | Debug.LogError($"{buildType} Failed (Transfer Error: Failed to transfer Build)"); 613 | } 614 | 615 | } 616 | #endregion 617 | } 618 | 619 | struct FileFTPData 620 | { 621 | public string Name; 622 | public string Size; 623 | } 624 | 625 | public enum BuildMode 626 | { 627 | Normal = 0, 628 | FTP = 1, 629 | FTPTransfer = 2, 630 | Run = 3, 631 | USB = 4, 632 | USBRun = 5, 633 | EmuTransfer = 6 634 | } 635 | 636 | [System.Serializable] 637 | enum FileState 638 | { 639 | Missing, 640 | Same, 641 | Size, 642 | } 643 | 644 | } -------------------------------------------------------------------------------- /Editor/PSVitaUtilitesSettings.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEditor; 3 | using System.IO; 4 | using System.Net; 5 | using System; 6 | 7 | namespace PSVitaUtilities.Settings 8 | { 9 | public class PSVitaUtilitiesSettings : EditorWindow 10 | { 11 | static bool Loaded = false; 12 | static string vitaDBJSON; 13 | static PSVitaUtilitiesSettings instance; 14 | static string buildCache = ""; 15 | 16 | bool showVitaSettings = true; 17 | bool showProjectSettings = true; 18 | 19 | #region Settings 20 | public static string PSVitaIP; 21 | public static string FTPLocation; 22 | public static string USBDriveLetter; 23 | public static string EmuStoragePath; 24 | public static bool ShowTrialWatermark; 25 | public static bool VitaBDCheck; 26 | public static bool KillAllAppsBeforeLaunch; 27 | public static bool BuildRunReboot; 28 | public static int RetryCount; 29 | public static bool SizeCheck; 30 | 31 | Vector2 scrollPos; 32 | //public PlayerSettings.PSVita.PSVitaPowerMode PowerMode = PlayerSettings.PSVita.PSVitaPowerMode.ModeA; 33 | 34 | public static string DeveloperName 35 | { 36 | get { return PlayerSettings.companyName; } 37 | set { PlayerSettings.companyName = value; } 38 | } 39 | public static string ProductName 40 | { 41 | get { return PlayerSettings.productName; } 42 | set { PlayerSettings.productName = value; } 43 | } 44 | public static string ShortTitle 45 | { 46 | get { return PlayerSettings.PSVita.shortTitle; } 47 | set { PlayerSettings.PSVita.shortTitle = value; } 48 | } 49 | public static string TitleID 50 | { 51 | get 52 | { 53 | string[] temp = PlayerSettings.PSVita.contentID.Split('-', '_'); 54 | return temp[1]; 55 | } 56 | 57 | set 58 | { 59 | string[] temp = PlayerSettings.PSVita.contentID.Split('-', '_'); 60 | PlayerSettings.PSVita.contentID = temp[0] + "-" + value + "_" + temp[2] + "-" + temp[3]; 61 | } 62 | } 63 | //public static PlayerSettings.PSVita.PSVitaPowerMode PowerMode 64 | //{ 65 | // get { return PlayerSettings.PSVita.powerMode; } 66 | // set { PlayerSettings.PSVita.powerMode = value; } 67 | //} 68 | #endregion 69 | 70 | #region Creating Editor Window 71 | [MenuItem("PSVita/Settings")] 72 | public static void StartWindow() 73 | { 74 | buildCache = Application.dataPath.TrimEnd("Assets".ToCharArray()) + "/Build/BuildCache"; 75 | 76 | PSVitaUtilitiesSettings window = (PSVitaUtilitiesSettings)GetWindow(typeof(PSVitaUtilitiesSettings)); 77 | GUIContent windowContent = new GUIContent(); 78 | windowContent.text = "PSVita Settings"; 79 | window.titleContent = windowContent; 80 | window.minSize = new Vector2(450, 350); 81 | window.Show(); 82 | 83 | instance = window; 84 | } 85 | #endregion 86 | 87 | void OnGUI() 88 | { 89 | #region Initialisation 90 | if (!Loaded) 91 | { 92 | LoadSettings(); 93 | Loaded = true; 94 | } 95 | #endregion 96 | 97 | #region Allowing for Bold Foldouts 98 | GUIStyle boldFoldout = EditorStyles.foldout; 99 | boldFoldout.fontStyle = FontStyle.Bold; 100 | #endregion 101 | 102 | #region Drawing Editor Window 103 | GUILayout.BeginVertical(); 104 | scrollPos = EditorGUILayout.BeginScrollView(scrollPos); 105 | 106 | #region Shilling 107 | GUILayout.Space(16); 108 | GUILayout.Label("Please keep in mind that this package makes use of the PSVita Companion Plugin for Vita Control and Build and Run functionality. \nIt can be downloaded through AutoPlugin 2 on the Vita.", EditorStyles.wordWrappedLabel); 109 | 110 | EditorLine(); 111 | 112 | if (FlexiButton("Link to PSVita Utilites on GitHub")) 113 | Application.OpenURL("https://github.com/GlitcherOG/PSVita-Unity-Utilities"); 114 | #endregion 115 | 116 | EditorLine(); 117 | 118 | #region Utility Settings 119 | showVitaSettings = EditorGUILayout.Foldout(showVitaSettings, "Utility Settings", boldFoldout); 120 | if (showVitaSettings) 121 | { 122 | #region Input Text Fields 123 | EditorGUILayout.LabelField("FTP Settings"); 124 | PSVitaIP = EditorGUILayout.TextField("PSVita IP", PSVitaIP); 125 | FTPLocation = EditorGUILayout.TextField("FTP Build Location", FTPLocation); 126 | RetryCount = EditorGUILayout.IntField("FTP Retries", RetryCount); 127 | 128 | //GUILayout.Space(8); 129 | //EditorGUILayout.LabelField("Emulator Settings"); 130 | //EmuStoragePath = EditorGUILayout.TextField("Emulator Storage Path", EmuStoragePath); 131 | 132 | GUILayout.Space(8); 133 | EditorGUILayout.LabelField("USB Settings"); 134 | USBDriveLetter = EditorGUILayout.TextField("USB Drive Letter", USBDriveLetter); 135 | #endregion 136 | 137 | EditorLine(); 138 | 139 | #region Setting Bools 140 | ShowTrialWatermark = EditorGUILayout.ToggleLeft("Enable \"Trial Version\" watermark", ShowTrialWatermark, EditorStyles.wordWrappedLabel); 141 | VitaBDCheck = EditorGUILayout.ToggleLeft("Check TitleID against the VitaDB", VitaBDCheck, EditorStyles.wordWrappedLabel); 142 | BuildRunReboot = EditorGUILayout.ToggleLeft("Reboot Vita before starting game", BuildRunReboot, EditorStyles.wordWrappedLabel); 143 | EditorIndentBlock("This will automatically reboot the Vita after Build and Run finishes transferring files. May reduce errors."); 144 | KillAllAppsBeforeLaunch = EditorGUILayout.ToggleLeft("Kill all apps on Vita before launching game remotely", KillAllAppsBeforeLaunch, EditorStyles.wordWrappedLabel); 145 | SizeCheck = EditorGUILayout.ToggleLeft("Check Files Before Transfering", SizeCheck, EditorStyles.wordWrappedLabel); 146 | EditorIndentBlock("This will check if the file being transferring already exists in the Build and Run Cache, and skip them if they match."); 147 | #endregion 148 | 149 | EditorLine(); 150 | 151 | #region ButtonBois 152 | EditorGUILayout.BeginHorizontal(); 153 | 154 | if (FlexiButton("Refresh VitaDB")) 155 | DownloadVitaDBCache(); 156 | if (FlexiButton("Delete Build and Run Cache")) 157 | Directory.Delete(buildCache, true); 158 | 159 | EditorGUILayout.EndHorizontal(); 160 | #endregion 161 | } 162 | #endregion 163 | 164 | EditorLine(); 165 | 166 | #region Project Settings 167 | showProjectSettings = EditorGUILayout.Foldout(showProjectSettings, "Project Settings", boldFoldout); 168 | if (showProjectSettings) 169 | { 170 | DeveloperName = EditorGUILayout.TextField("Developed by", DeveloperName); 171 | ProductName = EditorGUILayout.TextField("Title", ProductName); 172 | ShortTitle = EditorGUILayout.TextField("Short Title", ShortTitle); 173 | TitleID = EditorGUILayout.TextField("TitleID", TitleID); 174 | //PowerMode = (PlayerSettings.PSVita.PSVitaPowerMode)EditorGUILayout.EnumPopup("Power Mode", PowerMode); 175 | } 176 | #endregion 177 | 178 | EditorLine(); 179 | 180 | if (FlexiButton("Save")) 181 | SaveSettings(); 182 | 183 | GUILayout.Space(16); // This last space is to just have the Save button not be touching the bottom of the window 184 | 185 | GUILayout.EndVertical(); 186 | EditorGUILayout.EndScrollView(); 187 | #endregion 188 | } 189 | 190 | #region Saving and Loading 191 | public static void LoadSettings() 192 | { 193 | PSVitaIP = EditorPrefs.GetString("PSVitaIP", "0.0.0.0"); 194 | ShowTrialWatermark = EditorPrefs.GetBool("ShowTrialWatermark", false); 195 | FTPLocation = EditorPrefs.GetString("FTPLocation", "ux0:/"); 196 | USBDriveLetter = EditorPrefs.GetString("USBDriveLetter", "H:/"); 197 | VitaBDCheck = EditorPrefs.GetBool("VitaDBCheck", true); 198 | RetryCount = EditorPrefs.GetInt("Retying", 3); 199 | KillAllAppsBeforeLaunch = EditorPrefs.GetBool("KillAllAppsBeforeLaunch", true); 200 | BuildRunReboot = EditorPrefs.GetBool("BuildRunReboot", false); 201 | SizeCheck = EditorPrefs.GetBool("SizeCheck", true); 202 | EmuStoragePath = EditorPrefs.GetString("EmuStoragePath", Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)+"\\Vita3K\\Vita3K"); 203 | //PowerMode = (PlayerSettings.PSVita.PSVitaPowerMode)EditorPrefs.GetInt("PowerMode", 1); 204 | } 205 | public static void SaveSettings() 206 | { 207 | EditorPrefs.SetString("PSVitaIP", PSVitaIP); 208 | EditorPrefs.SetBool("ShowTrialWatermark", ShowTrialWatermark); 209 | if (FTPLocation == "") 210 | FTPLocation = "ux0:/"; 211 | EditorPrefs.SetString("FTPLocation", FTPLocation); 212 | if (USBDriveLetter == "") 213 | USBDriveLetter = "H:/"; 214 | EditorPrefs.SetString("USBDriveLetter", USBDriveLetter); 215 | EditorPrefs.SetBool("VitaDBCheck", VitaBDCheck); 216 | EditorPrefs.SetInt("Retying", RetryCount); 217 | if (TitleID.Length != 9) 218 | { 219 | Debug.LogError("Title ID is recomended to be 9 characters long with 4 letters and 5 numbers (E.g. ABCD12345)"); 220 | EditorApplication.Beep(); 221 | } 222 | EditorPrefs.SetBool("KillAllAppsBeforeLaunch", KillAllAppsBeforeLaunch); 223 | EditorPrefs.SetBool("BuildRunReboot", BuildRunReboot); 224 | EditorPrefs.SetBool("SizeCheck", SizeCheck); 225 | //EditorPrefs.SetInt("PowerMode", (int)PowerMode); 226 | if (EmuStoragePath == "") 227 | EmuStoragePath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\Vita3K\\Vita3K"; 228 | EditorPrefs.SetString("EmuStoragePath", EmuStoragePath); 229 | Debug.Log("Settings Saved"); 230 | 231 | CheckValidTitleID(); 232 | if (instance != null) 233 | instance.ShowNotification(new GUIContent("Saved")); 234 | } 235 | #endregion 236 | 237 | public static bool CheckValidTitleID(bool skipVitaDBCheck = false) 238 | { 239 | if (TitleID == "" || TitleID.Length != 9) return false; 240 | 241 | if (TitleID == "ABCD12345") 242 | { 243 | if (!EditorUtility.DisplayDialog("TitleID", "This project is using the default Title ID and may override any other application using that Title ID.\nDo you want to continue?", "Yes", "No")) 244 | return false; 245 | } 246 | if (!skipVitaDBCheck && VitaBDCheck) 247 | { 248 | bool idFound = false; // Needed in case multiple of the same ID exists on VitaDB. This is actually the case for "ABCD12345", but we have our own check for that above. 249 | 250 | string json = File.ReadAllText(Application.dataPath + "/Editor/vitadb.txt"); 251 | 252 | VitaDBItem[] vitaDBItems = VitaDBItem.LoadVitaDBTitleIDs(json); 253 | 254 | foreach (VitaDBItem item in vitaDBItems) 255 | { 256 | if (item.titleid == TitleID && idFound == false) 257 | { 258 | idFound = true; 259 | if (!EditorUtility.DisplayDialog("Title ID", "The Title ID you have entered appears to already exist on VitaDB. Using this ID may cause issues in the future in case you install those applications.\nDo you want to continue with this Title ID?", "Yes", "No")) 260 | return false; 261 | } 262 | } 263 | } 264 | return true; 265 | } 266 | 267 | #region Loading TitleIDs from VitaDB 268 | public void DownloadVitaDBCache() 269 | { 270 | Debug.Log("Downloading VitaDB Cache..."); 271 | WebClient client = new WebClient(); 272 | client.Headers.Add("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)"); 273 | client.DownloadFile("https://rinnegatamante.it/vitadb/list_hbs_json.php", Application.dataPath + "/Editor/vitadb.txt"); 274 | Debug.Log("Downloaded"); 275 | } 276 | #endregion 277 | 278 | #region Bane's Handy Editor Functions 279 | void EditorIndentBlock(string text, int indentLevel = 1) 280 | { 281 | EditorGUI.indentLevel = indentLevel; 282 | EditorGUILayout.LabelField(text, EditorStyles.wordWrappedLabel); 283 | EditorGUI.indentLevel = 0; 284 | } 285 | void EditorLine() => EditorGUILayout.LabelField("", GUI.skin.horizontalSlider); 286 | bool FlexiButton(string label, GUILayoutOption[] options = null) 287 | { 288 | #region Default Options 289 | GUILayoutOption[] defaultOptions = { GUILayout.MinWidth(2), GUILayout.MaxWidth(260), GUILayout.MinHeight(16), GUILayout.ExpandWidth(true) }; 290 | if (options == null) 291 | options = defaultOptions; 292 | #endregion 293 | 294 | bool clicked = false; 295 | EditorGUILayout.BeginHorizontal(); 296 | GUILayout.FlexibleSpace(); 297 | if (GUILayout.Button(label, options)) 298 | clicked = true; 299 | GUILayout.FlexibleSpace(); 300 | EditorGUILayout.EndHorizontal(); 301 | return clicked; 302 | } 303 | #endregion 304 | } 305 | 306 | #region Helper Classes 307 | [System.Serializable] 308 | public class VitaDBItem 309 | { 310 | public string titleid; 311 | 312 | public static VitaDBItem[] LoadVitaDBTitleIDs(string jsonString) => JsonHelper.FromJson(jsonString); 313 | } 314 | 315 | /// 316 | /// Json Helper tools, taken from https://stackoverflow.com/a/36244111 317 | /// 318 | public static class JsonHelper 319 | { 320 | public static T[] FromJson(string json) 321 | { 322 | Wrapper wrapper = JsonUtility.FromJson>(FixJson(json)); 323 | return wrapper.Items; 324 | } 325 | #pragma warning disable 0649 326 | [System.Serializable] 327 | private class Wrapper 328 | { 329 | public T[] Items; 330 | } 331 | #pragma warning restore 0649 332 | /// 333 | /// Formats JSON in a way that makes the FromJson thing above work 334 | /// 335 | /// 336 | /// 337 | static string FixJson(string value) => "{\"Items\":" + value + "}"; 338 | } 339 | #endregion 340 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PSVita Unity Utilities 2 | 3 | Tools to help speed up PSVita Unity Development 4 | 5 | ## Features 6 | 7 | - VPK Building 8 | - FTP Tranfering 9 | - Build and Running (WIP) 10 | - Vita Control 11 | - Easy Access to Important Project Settings like TitleID 12 | - Checking TitleID Against the VitaDB 13 | 14 | ## Requirements 15 | 16 | To use the project you are required to: 17 | - Set the Scripting Runtime Version .NET 4.x Equivalent 18 | - Switch To PSVita in the build settings and make sure its set to PC Hosted 19 | - To improve chances of FTP transfering going through in vita settings and in power save settings set standby mode to 10-30 mins and turn off use Wifi in power save mode 20 | 21 | Note: These tools were made for use with Unity 2018.2.19f1 but may work on older versions. 22 | Unity 2017 currently requires Title Check against VitaDB be disabled in the settings. 23 | 24 | ## Installation 25 | 26 | Just import the Unity Package into the Project or download the git and add the editor folder and mcs.rsp to the asset folder of your project 27 | 28 | ## Authors 29 | - [GlitcherOG](https://github.com/GlitcherOG) 30 | - [Jordy3D](https://github.com/Jordy3D) 31 | 32 | ## Special Thanks 33 | 34 | - [UnityTools by SilicaAndPina](https://bitbucket.org/SilicaAndPina/unitytools/src/master/) 35 | - [Vitacompanion by devnoname120](https://github.com/devnoname120/vitacompanion) 36 | 37 | -------------------------------------------------------------------------------- /mcs.rsp: -------------------------------------------------------------------------------- 1 | -r:System.IO.Compression.FileSystem.dll --------------------------------------------------------------------------------