Пример #1
0
        private static ValidateResult checkCategoryMatchesPkgType(Gp4Project proj, string dir, ParamSfo sfo)
        {
            var  sfoCategory = sfo["CATEGORY"].ToString();
            var  pkgType     = proj.volume.Type;
            bool ok          = true;

            switch (pkgType)
            {
            case VolumeType.pkg_ps4_app:
                if (!sfoCategory.StartsWith("g"))
                {
                    ok = false;
                }
                break;

            case VolumeType.pkg_ps4_ac_data:
            case VolumeType.pkg_ps4_ac_nodata:
            case VolumeType.pkg_ps4_theme:
            case VolumeType.pkg_ps4_sf_theme:
                if (sfoCategory != "ac")
                {
                    ok = false;
                }
                break;
            }
            if (!ok)
            {
                return(ValidateResult.Warning(
                           $"SFO CATEGORY {sfoCategory} is not valid for PKG volume type {pkgType}."));
            }
            return(null);
        }
Пример #2
0
 private static ValidateResult checkContentIdLength(Gp4Project proj, string dir)
 {
     if (proj.volume.Package.ContentId.Length != 36)
     {
         return(ValidateResult.Fatal("PKG Content ID must be 36 characters long."));
     }
     return(null);
 }
Пример #3
0
 private static ValidateResult checkPasscode(Gp4Project proj, string dir)
 {
     if (proj.volume.Package.Passcode.Length != 32)
     {
         return(ValidateResult.Fatal("Passcode must be 32 characters long."));
     }
     return(null);
 }
Пример #4
0
        private static ValidateResult checkAllFilesExist(Gp4Project proj, string dir)
        {
            var missingFiles = proj.files.Items
                               .Where(f => Path.Combine(dir, f.OrigPath) is string filePath && !File.Exists(filePath))
                               .Aggregate((string)null, (s, f) => s == null ? f.OrigPath : (s + ", " + f.OrigPath));

            if (missingFiles != null)
            {
                return(ValidateResult.Fatal("Could not find source file(s): " + missingFiles));
            }
            return(null);
        }
Пример #5
0
        private static ValidateResult checkContentIdMatchesTitleIdSfo(Gp4Project proj, string dir, ParamSfo sfo)
        {
            var sfoContentId = sfo["CONTENT_ID"].ToString();
            var sfoTitleId   = sfo["TITLE_ID"].ToString();

            if (sfoContentId.Substring(7).StartsWith(sfoTitleId))
            {
                return(null);
            }
            return(ValidateResult.Warning(
                       $"SFO TITLE_ID {sfoTitleId} does not match the CONTENT_ID {sfoContentId}."));
        }
Пример #6
0
        private static ValidateResult checkContentIdMatchesSfo(Gp4Project proj, string dir, ParamSfo sfo)
        {
            var pkgContentId = proj.volume.Package.ContentId;
            var sfoContentId = sfo["CONTENT_ID"].ToString();

            if (pkgContentId != sfoContentId)
            {
                return(ValidateResult.Warning(
                           $"PKG Content ID {pkgContentId} does not match CONTENT_ID {sfoContentId} in param.sfo."));
            }
            return(null);
        }
Пример #7
0
        private static ValidateResult checkContentIdFormat(Gp4Project proj, string dir)
        {
            var   pkgContentId = proj.volume.Package.ContentId;
            Regex contentIdReg = new Regex("^[A-Z]{2}[0-9]{4}-[A-Z]{4}[0-9]{5}_00-[A-Z0-9]{16}$");

            if (contentIdReg.IsMatch(pkgContentId))
            {
                return(null);
            }
            return(ValidateResult.Warning(
                       "PKG Content ID is the wrong format. " +
                       "Format should be XXYYYY-XXXXYYYYY_00-ZZZZZZZZZZZZZZZZ, where X is a letter, Y is a number, and Z is either."));
        }
Пример #8
0
        private static ValidateResult checkDuplicateFilenames(Gp4Project proj, string dir)
        {
            var dupeFiles = proj.files.Items
                            .GroupBy(f => f.TargetPath)
                            .Where(g => g.Count() > 1)
                            .Select(g => g.Key)
                            .Aggregate((string)null, (s, f) => s == null ? f : (s + ", " + f));

            if (dupeFiles != null)
            {
                return(ValidateResult.Fatal("PKG has duplicate filename(s): " + dupeFiles));
            }
            return(null);
        }
Пример #9
0
        public static List <ValidateResult> ValidateProject(Gp4Project proj, string projDir)
        {
            var ret = new List <ValidateResult>();

            foreach (var check in commonChecks)
            {
                var result = check(proj, projDir);
                if (result != null)
                {
                    ret.Add(result);
                }
            }
            // Checks with project and SFO file
            if (proj.files.Items.Where(f => f.TargetPath == "sce_sys/param.sfo").FirstOrDefault() is Gp4File sfoFile &&
                Path.Combine(projDir, sfoFile.OrigPath) is string sfoPath &&
                File.Exists(sfoPath))
            {
                ParamSfo sfoObject = null;
                try
                {
                    using (var f = File.OpenRead(sfoPath))
                    {
                        sfoObject = ParamSfo.FromStream(f);
                    }
                }
                catch (Exception e)
                {
                    ret.Add(ValidateResult.Fatal("Could not load param.sfo file: " + e.Message));
                }
                if (sfoObject != null)
                {
                    foreach (var check in sfoChecks)
                    {
                        var result = check(proj, projDir, sfoObject);
                        if (result != null)
                        {
                            ret.Add(result);
                        }
                    }
                }
            }
Пример #10
0
        private static ValidateResult checkPkgVolumeType(Gp4Project proj, string dir)
        {
            var pkgType = proj.volume.Type;

            switch (pkgType)
            {
            case VolumeType.pkg_ps4_app:
                break;

            case VolumeType.pkg_ps4_ac_data:
                break;

            case VolumeType.pkg_ps4_ac_nodata:
                break;

            default:
                return(ValidateResult.Fatal(
                           "Unsupported PKG volume type: " + pkgType));
            }
            return(null);
        }
Пример #11
0
        /// <summary>
        /// Creates a new, empty GP4 project with the given VolumeType.
        /// </summary>
        /// <param name="type">The type of project to make</param>
        /// <returns>A new blank project with defaults for the given VolumeType</returns>
        public static Gp4Project Create(VolumeType type)
        {
            var proj = new Gp4Project
            {
                files   = new Files(),
                Format  = "gp4",
                RootDir = new List <Dir>(),
                version = 1000,
                volume  = new Volume
                {
                    volume_ts = DateTime.UtcNow.ToString("s").Replace('T', ' '),
                    Package   = new PackageInfo
                    {
                        ContentId = "XXXXXX-CUSA00000_00-ZZZZZZZZZZZZZZZZ",
                        Passcode  = "00000000000000000000000000000000"
                    }
                }
            };

            proj.SetType(type);
            return(proj);
        }
Пример #12
0
        /// <summary>
        /// Writes the GP4 project xml to the given stream at the stream's current position.
        /// </summary>
        /// <param name="proj">The project to write</param>
        /// <param name="s">The stream to write the project to</param>
        public static void WriteTo(Gp4Project proj, System.IO.Stream s)
        {
            XmlSerializer mySerializer = new XmlSerializer(typeof(Gp4Project));

            mySerializer.Serialize(s, proj);
        }
Пример #13
0
        public static void CreateProjectFromPKG(string outputDir, MemoryMappedFile pkgFile, string passcode = null)
        {
            Directory.CreateDirectory(outputDir);
            Pkg pkg;

            using (var f = pkgFile.CreateViewStream(0, 0, MemoryMappedFileAccess.Read))
                pkg = new PkgReader(f).ReadPkg();

            passcode = passcode ?? "00000000000000000000000000000000";

            // Initialize project parameters
            var project = Gp4Project.Create(ContentTypeToVolumeType(pkg.Header.content_type));

            project.volume.Package.Passcode    = passcode;
            project.volume.Package.ContentId   = pkg.Header.content_id;
            project.volume.Package.AppType     = project.volume.Type == VolumeType.pkg_ps4_app ? "full" : null;
            project.volume.Package.StorageType = project.volume.Type == VolumeType.pkg_ps4_app ? "digital50" : null;

            if (pkg.Header.content_type == ContentType.AC || pkg.Header.content_type == ContentType.AL)
            {
                pkg.LicenseDat.DecryptSecretWithDebugKey();
                var entitlementKey = new byte[16];
                Buffer.BlockCopy(pkg.LicenseDat.Secret, 0x70, entitlementKey, 0, 16);
                pkg.LicenseDat.EncryptSecretWithDebugKey();
                project.volume.Package.EntitlementKey = entitlementKey.ToHexCompact();
            }

            // Extract entry filesystem
            var sys_dir     = Path.Combine(outputDir, "sce_sys");
            var sys_projdir = project.AddDir(null, "sce_sys");

            Directory.CreateDirectory(sys_dir);
            foreach (var meta in pkg.Metas.Metas)
            {
                // Skip entries that are auto-generated or that we don't know the filenames for
                if (GeneratedEntries.Contains(meta.id))
                {
                    continue;
                }
                if (!EntryNames.IdToName.ContainsKey(meta.id))
                {
                    continue;
                }

                var entryName = EntryNames.IdToName[meta.id];
                var filename  = Path.Combine(sys_dir, entryName);

                // Create directories for entries within directories
                if (entryName.Contains('/'))
                {
                    var entryDir = entryName.Substring(0, entryName.LastIndexOf('/'));
                    Directory.CreateDirectory(Path.Combine(sys_dir, entryDir));
                    Dir d = sys_projdir;
                    foreach (var breadcrumb in entryDir.Split('/'))
                    {
                        d = project.AddDir(d, breadcrumb);
                    }
                }

                // Add the entry to the project
                project.files.Items.Add(new Gp4File()
                {
                    OrigPath   = "sce_sys/" + entryName,
                    TargetPath = "sce_sys/" + entryName
                });

                // Save to the filesystem
                using (var s = pkgFile.CreateViewStream(meta.DataOffset, meta.DataSize, MemoryMappedFileAccess.Read))
                    using (var entryFile = File.Create(filename))
                    {
                        s.CopyTo(entryFile);
                    }
            }

            // Fixup the param.sfo
            using (var f = File.Open(Path.Combine(outputDir, "sce_sys/param.sfo"), FileMode.Open))
            {
                var sfo         = SFO.ParamSfo.FromStream(f);
                var pubtoolinfo = (sfo["PUBTOOLINFO"] as SFO.Utf8Value).Value;
                var c_date      = "";
                var c_time      = "";
                foreach (var info in pubtoolinfo.Split(','))
                {
                    var info2 = info.Split('=');
                    switch (info2[0])
                    {
                    case "c_date":
                        c_date =
                            info2[1].Substring(0, 4) + "-" +
                            info2[1].Substring(4, 2) + "-" +
                            info2[1].Substring(6, 2);
                        break;

                    case "c_time":
                        c_time = " " +
                                 info2[1].Substring(0, 2) + ":" +
                                 info2[1].Substring(2, 2) + ":" +
                                 info2[1].Substring(4, 2);
                        break;
                    }
                }
                project.volume.Package.CreationDate = c_date + c_time;
                sfo["PUBTOOLVER"]  = null;
                sfo["PUBTOOLINFO"] = null;
                sfo.Write(f);
            }

            // Extract files from the PFS filesystem
            byte[] ekpfs;
            if (pkg.CheckPasscode(passcode))
            {
                ekpfs = Crypto.ComputeKeys(pkg.Header.content_id, passcode, 1);
            }
            else
            {
                ekpfs = pkg.GetEkpfs();
            }
            using (var va = pkgFile.CreateViewAccessor((long)pkg.Header.pfs_image_offset, (long)pkg.Header.pfs_image_size, MemoryMappedFileAccess.Read))
            {
                var outerPfs = new PfsReader(va, pkg.Header.pfs_flags, ekpfs);
                var inner    = new PfsReader(new PFSCReader(outerPfs.GetFile("pfs_image.dat").GetView()));
                // Convert PFS image timestamp from UNIX time and save it in the project
                project.volume.TimeStamp = new DateTime(1970, 1, 1)
                                           .AddSeconds(inner.Header.InodeBlockSig.Time1_sec);
                var uroot       = inner.GetURoot();
                Dir dir         = null;
                var projectDirs = new Queue <Dir>();
                var pfsDirs     = new Queue <PfsReader.Dir>();
                pfsDirs.Enqueue(uroot);
                projectDirs.Enqueue(dir);
                while (pfsDirs.Count > 0)
                {
                    dir = projectDirs.Dequeue();
                    if (dir != null)
                    {
                        Directory.CreateDirectory(Path.Combine(outputDir, dir.Path));
                    }
                    foreach (var f in pfsDirs.Dequeue().children)
                    {
                        if (f is PfsReader.Dir d)
                        {
                            pfsDirs.Enqueue(d);
                            projectDirs.Enqueue(project.AddDir(dir, d.name));
                        }
                        else if (f is PfsReader.File file)
                        {
                            // Remove "/uroot/"
                            var path = file.FullName.Substring(7);
                            project.files.Items.Add(new Gp4File()
                            {
                                OrigPath   = path,
                                TargetPath = path
                            });
                            file.Save(Path.Combine(outputDir, path));
                        }
                    }
                }
            }

            // Last step: save the project file
            using (var f = File.Create(Path.Combine(outputDir, "Project.gp4")))
            {
                Gp4Project.WriteTo(project, f);
            }
        }