/// <summary>
        /// Treats this file as a zip file and extracts it to the specified target directory. This file and
        /// the target directory do NOT need to be in the same file system.
        /// If this file is not a valid zip archive, or any error occurs, then false is returned.
        /// </summary>
        public static bool ExtractZip(this FSFile zipFile, FSDir targetDir,
                                      ZipExtractionOptions options)
        {
            // First open the FSFile for reading
            Stream fsFileStream = zipFile.OpenReadOnly();

            // Create a zip archive (for reading) from the stream
            ZipArchive zipA = null;

            try
            {
                zipA = new ZipArchive(fsFileStream, ZipArchiveMode.Read);
            }
            catch (Exception)
            {
                fsFileStream.Dispose();
                return(false);
            }

            // Call the utility function to do the actual extraction
            ExtractZip(zipA, targetDir, options);

            // Clean up and return
            zipA.Dispose();
            fsFileStream.Dispose();
            return(true);
        }
Beispiel #2
0
        public static FSDir MakeRoot(ParamSfo sfo = null, FSFile[] sc0Files = null)
        {
            if (sfo == null)
            {
                sfo = ParamSfo.DefaultAC;
            }
            var root   = new FSDir();
            var sysDir = new FSDir()
            {
                name   = "sce_sys",
                Parent = root,
            };

            sysDir.Files.Add(new FSFile(s => sfo.Write(s), "param.sfo", sfo.FileSize)
            {
                Parent = sysDir
            });
            if (sc0Files != null)
            {
                foreach (var f in sc0Files)
                {
                    sysDir.Files.Add(f);
                    f.Parent = sysDir;
                }
            }
            root.Dirs.Add(sysDir);
            return(root);
        }
Beispiel #3
0
        public static void DLCSongToFsFiles(DLCSong song, FSDir songsDir)
        {
            var shortname = song.SongData.Shortname;
            var songDir   = new FSDir()
            {
                name = shortname, Parent = songsDir
            };

            songsDir.Dirs.Add(songDir);
            songDir.Files.Add(WriterToFile(
                                  $"{shortname}.lipsync_ps4",
                                  s => new LipsyncWriter(s).WriteStream(song.Lipsync)));
            songDir.Files.Add(new FSFile(
                                  s => { using (var mogg = song.Mogg.GetStream()) mogg.CopyTo(s); },
                                  $"{shortname}.mogg",
                                  song.Mogg.Size));
            var moggFileString = Encoding.UTF8.GetBytes(song.MoggDta.ToFileString());

            songDir.Files.Add(new FSFile(
                                  s => s.Write(moggFileString, 0, moggFileString.Length),
                                  $"{shortname}.mogg.dta",
                                  moggFileString.Length));
            var moggSongFileString = Encoding.UTF8.GetBytes(song.MoggSong.ToFileString());

            songDir.Files.Add(new FSFile(
                                  s => s.Write(moggSongFileString, 0, moggSongFileString.Length),
                                  $"{shortname}.moggsong",
                                  moggSongFileString.Length));
            songDir.Files.Add(WriterToFile(
                                  $"{shortname}.rbmid_ps4",
                                  s => RBMidWriter.WriteStream(song.RBMidi, s)));
            songDir.Files.Add(WriterToFile(
                                  $"{shortname}.rbsong",
                                  s => new RBSongWriter(s).WriteStream(song.RBSong)));
            songDir.Files.Add(WriterToFile(
                                  $"{shortname}.songdta_ps4",
                                  s => SongDataWriter.WriteStream(song.SongData, s)));
            if (song.SongData.AlbumArt)
            {
                songDir.Files.Add(WriterToFile(
                                      $"{shortname}.png_ps4",
                                      s => Texture.TextureWriter.WriteStream(song.Artwork, s)));
            }
            foreach (var f in songDir.Files)
            {
                f.Parent = songDir;
            }
        }
Beispiel #4
0
        public FilesWebService(FSDir filesRoot,
                               Action <FilesWebService, string, WebRequest> onFileNotFound = null)
        {
            // Ensure that the root is non-null
            if (null == filesRoot)
            {
                throw new ArgumentNullException(
                          "filesRoot",
                          "Root folder of FilesWebService cannot be null");
            }

            m_root           = filesRoot;
            m_onFileNotFound = onFileNotFound;

            // We have a default page builder for directory listings
            m_onNeedDirList = this.DefaultBuildDirHTML;
        }
Beispiel #5
0
        private FSEntry GetEntry(string uri)
        {
            // Request URIs must never contain \ (only /)
            if (uri.Contains("\\"))
            {
                return(null);
            }

            // Make sure the URI starts with the service URI, and prodived it does, strip
            // off that part.
            if (!uri.StartsWith(ServiceURI))
            {
                return(null);
            }
            uri = uri.Substring(ServiceURI.Length);

            // Get the file or directory from the URI
            string[] parts = uri.Split(
                new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
            FSDir dir = m_root, parent = null;

            for (int i = 0; i < parts.Length; i++)
            {
                parent = dir;
                dir    = dir.GetDir(parts[i]);

                // Should we get a null directory we have 2 cases:
                // 1. This is NOT the last loop iteration, implying that we've hit a not found.
                // 2. This IS the last loop iteration, meaning that the last part of the file
                //    string may be a file name and/or may be a not found. We fall through to
                //    the code after this loop in that case.
                if (null == dir && i != parts.Length - 1)
                {
                    return(null);
                }
            }

            // If 'dir' is non-null at this point then we return it
            if (null != dir)
            {
                return(dir);
            }

            // Coming here implies it's a file name
            return(parent.GetFile(parts[parts.Length - 1]));
        }
Beispiel #6
0
        public string GetHREF(FSDir directory, bool urlEncoded)
        {
            // The service URL is not required to end with '/' but we must have this
            // after it when generating an HREF for the page.
            string start = ServiceURI;

            if (!start.EndsWith("/"))
            {
                start += "/";
            }

            // Special case for root
            if (object.ReferenceEquals(directory, m_root))
            {
                return(urlEncoded ? Uri.EscapeUriString(start) : start);
            }

            string href = directory.Name;
            FSDir  dir  = directory.Parent;

            while (!object.ReferenceEquals(dir, m_root))
            {
                href = dir.Name + "/" + href;
                dir  = dir.Parent;

                // We should always hit the root before null, provided the file is in fact
                // somewhere within the hosted directory. Should we get null then the file
                // is outside of the shared content and it's an invalid call.
                if (null == dir)
                {
                    throw new InvalidOperationException(
                              "GetHREF was called for a directory that's not in the shared content directories");
                }
            }
            href = start + href;

            if (urlEncoded)
            {
                return(Uri.EscapeUriString(href));
            }
            return(href);
        }
Beispiel #7
0
        private static FSDir MakeRoot(ParamSfo sfo = null)
        {
            if (sfo == null)
            {
                sfo = ParamSfo.DefaultAC;
            }
            var root   = new FSDir();
            var sysDir = new FSDir()
            {
                name   = "sce_sys",
                Parent = root,
            };

            sysDir.Files.Add(new FSFile(s => sfo.Write(s), "param.sfo", sfo.FileSize)
            {
                Parent = sysDir
            });
            root.Dirs.Add(sysDir);
            return(root);
        }
Beispiel #8
0
        public void ManyFilePkg()
        {
            const int NumFiles = 5000;

            using (var pkgFile = new TempFile())
            {
                var             buf        = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
                Action <Stream> fileWriter = s => s.Write(buf, 0, 16);
                var             rootDir    = TestHelper.MakeRoot();
                var             filesDir   = new FSDir()
                {
                    Parent = rootDir, name = "files"
                };
                rootDir.Dirs.Add(filesDir);
                for (int i = 0; i < NumFiles; i++)
                {
                    filesDir.Files.Add(new FSFile(fileWriter, $"file_{i}", 16)
                    {
                        Parent = filesDir
                    });
                }
                new PkgBuilder(TestHelper.MakeProperties(RootDir: rootDir)).Write(pkgFile.Path, Console.WriteLine);
                int foundFiles = 0;
                TestHelper.OpenPkgFilesystem(pkgFile.Path, innerPfs =>
                {
                    var testBuf = new byte[16];
                    foreach (var f in innerPfs.GetURoot().GetAllFiles())
                    {
                        foundFiles++;
                        using (var view = f.GetView())
                        {
                            view.Read(0, testBuf, 0, 16);
                            CollectionAssert.AreEqual(buf, testBuf, $"Expected {buf.AsHexCompact()}, got {testBuf.AsHexCompact()}");
                        }
                    }
                });

                Assert.AreEqual(NumFiles, foundFiles);
            }
        }
Beispiel #9
0
        public static void BuildPkg(List <DLCSong> songs, string contentId, string desc, bool eu, string output, Action <string> logger)
        {
            var shortnames = new List <string>(songs.Count);
            var root       = new FSDir();
            var sys        = new FSDir()
            {
                name = "sce_sys", Parent = root
            };

            root.Dirs.Add(sys);
            var songsDir = new FSDir()
            {
                name = "songs", Parent = root
            };

            root.Dirs.Add(songsDir);
            foreach (var song in songs)
            {
                DLCSongToFsFiles(song, songsDir);
            }
            var paramSfo = MakeParamSfo(contentId, desc, eu);

            sys.Files.Add(new FSFile(s => s.Write(paramSfo, 0, paramSfo.Length), "param.sfo", paramSfo.Length)
            {
                Parent = sys
            });
            new LibOrbisPkg.PKG.PkgBuilder(new LibOrbisPkg.PKG.PkgProperties
            {
                ContentId       = contentId,
                CreationDate    = DateTime.UtcNow,
                TimeStamp       = DateTime.UtcNow,
                UseCreationTime = true,
                EntitlementKey  = "00000000000000000000000000000000",
                Passcode        = "00000000000000000000000000000000",
                RootDir         = root,
                VolumeType      = LibOrbisPkg.GP4.VolumeType.pkg_ps4_ac_data,
            }).Write(output, logger);
            logger("Done!");
        }
Beispiel #10
0
 private static PkgProperties MakeProperties(
     string ContentId      = "UP0000-TEST00000_00-0000000000000000",
     DateTime?CreationDate = null,
     VolumeType VolumeType = VolumeType.pkg_ps4_ac_data,
     string EntitlementKey = "00000000000000000000000000000000",
     string Passcode       = "00000000000000000000000000000000",
     DateTime?TimeStamp    = null,
     bool UseCreationTime  = true,
     FSDir RootDir         = null)
 {
     return(new PkgProperties()
     {
         ContentId = ContentId,
         CreationDate = CreationDate ?? PkgDate,
         VolumeType = VolumeType,
         EntitlementKey = EntitlementKey,
         Passcode = Passcode,
         TimeStamp = TimeStamp ?? PkgDate,
         UseCreationTime = UseCreationTime,
         RootDir = RootDir ?? MakeRoot(),
     });
 }
        private static ZipExtractionStats ExtractZip(ZipArchive zipA, FSDir targetDirectory, ZipExtractionOptions options)
        {
            // Keep track of the number of entries encountered and extracted
            ZipExtractionStats stats = new ZipExtractionStats();

            // Loop through entries and extract
            foreach (ZipArchiveEntry entry in zipA.Entries)
            {
                stats.NumEntriesSeen++;
                bool  skipThisEntry = false;
                FSDir targetDir     = targetDirectory;

                // Split the full name on '/'
                string[] pieces = entry.FullName.Split('/');
                int      i;
                for (i = 0; i < pieces.Length - 1; i++)
                {
                    targetDir = targetDir.CreateDir(pieces[i]);
                    if (null == targetDir)
                    {
                        skipThisEntry = true;
                        break;
                    }
                }

                // Skip this entry if need be
                if (skipThisEntry)
                {
                    continue;
                }

                // At this point pieces[i] is the file we need to create in targetDir
                if (targetDir.GetFile(pieces[i]) != null && !options.Overwrite)
                {
                    // We can't overwrite the file
                    continue;
                }
                FSFile fileForEntry = targetDir.CreateFile(pieces[i]);
                if (null == fileForEntry)
                {
                    continue;
                }

                // Open the zip entry stream for reading and the FSFile for writing. Notice that despite
                // the possibility of failure to open the source stream from the zip file entry, we've
                // already created the FSFile in the target directory. This is intentional. If we fail
                // to extract a file, then a 0-byte placeholder is desired (for now, may change later).
                Stream src = entry.Open();
                if (src == null)
                {
                    continue;
                }
                Stream dst = fileForEntry.OpenReadWrite();
                if (dst == null)
                {
                    src.Dispose();
                    continue;
                }

                // Copy the contents
                byte[] buf = new byte[8192];
                while (true)
                {
                    int bytesRead = src.Read(buf, 0, buf.Length);
                    if (bytesRead <= 0)
                    {
                        break;
                    }
                    dst.Write(buf, 0, bytesRead);
                }

                // Clean up and increment number of files extracted
                src.Dispose();
                dst.Dispose();
                stats.NumEntriesExtracted++;
            }

            return(stats);
        }
Beispiel #12
0
        private string DefaultBuildDirHTML(FSDir directory)
        {
            // Build an HTML file listing
            var sb = new StringBuilder("<html>");

            // We'll need a bit of script if uploading is allowed
            if (m_allowUploads)
            {
                sb.AppendLine(
                    @"<script>
function selectedFileChanged(fileInput, urlPrefix)
{
    document.getElementById('uploadHdr').innerText = 'Uploading ' + fileInput.files[0].name + '...';

    // Need XMLHttpRequest to do the upload
    if (!window.XMLHttpRequest)
    {
        alert('Your browser does not support XMLHttpRequest. Please update your browser.');
        return;
    }

    // Hide the file selection controls while we upload
    var uploadControl = document.getElementById('uploader');
    if (uploadControl)
    {
        uploadControl.style.visibility = 'hidden';
    }

    // Build a URL for the request
    if (urlPrefix.lastIndexOf('/') != urlPrefix.length - 1)
    {
        urlPrefix += '/';
    }
    var uploadURL = urlPrefix + fileInput.files[0].name;

    // Create the service request object
    var req = new XMLHttpRequest();
    req.open('PUT', uploadURL);
    req.onreadystatechange = function()
    {
        document.getElementById('uploadHdr').innerText = 'Upload (request status == ' + req.status + ')';
        // Un-comment the line below and comment-out the line above if you want the page to 
        // refresh after the upload
        //location.reload();
    };
    req.send(fileInput.files[0]);
}
</script>
");
            }

            // Get the files and directories
            FSFile[] files = directory.GetFiles();
            FSDir[]  dirs  = directory.GetDirs();

            // Put all the directories first
            if (dirs.Length > 0)
            {
                sb.Append("<h3>Folders</h3><hr><br>");
            }
            foreach (var dir in dirs)
            {
                sb.AppendFormat(
                    "<a href=\"{0}\">{1}</a><br>",
                    GetHREF(dir, true), dir.Name);
            }

            // Then all files follow
            if (files.Length > 0)
            {
                sb.Append("<h3>Files</h3><hr><br>");
            }
            bool highlightRow = false;

            foreach (var file in files)
            {
                // Make an actual image in the page for an image file
                if (HasImgExt(file.Name))
                {
                    sb.AppendFormat("<img src=\"{0}\"><br>", GetHREF(file, true));
                }
                else
                {
                    sb.AppendFormat(
                        "<div style=\"padding:10 px;{0}\"> <a href=\"{1}\">{2}</a></div> <br>",
                        highlightRow ? " background-color:#F0F0F0;" : string.Empty,
                        GetHREF(file, true), file.Name);
                }

                highlightRow = !highlightRow;
            }

            // If uploading is allowed, put the uploader at the bottom
            if (m_allowUploads)
            {
                sb.AppendFormat(
                    "<hr><h3 id='uploadHdr'>Upload</h3><br>" +
                    "<input id=\"uploader\" type='file' " +
                    "onchange='selectedFileChanged(this,\"{0}\")' /><hr>",
                    GetHREF(directory, true));
            }

            sb.Append("</html>");
            return(sb.ToString());
        }
Beispiel #13
0
        /// <summary>
        /// Request handler function
        /// </summary>
        public override void Handler(WebRequest req)
        {
            if (m_disposed)
            {
                throw new ObjectDisposedException("FileWebServer");
            }

            if ("POST" == req.Method)
            {
                HandlePost(req);
                return;
            }

            // Handle uploads
            if ("PUT" == req.Method)
            {
                HandlePut(req);
                return;
            }

            // If it's anything other than GET at this point then it's not implemented
            if (req.Method != "GET")
            {
                m_logger.WriteLine(
                    "  Method was \"" + req.Method + "\" -> sending not implemented response");
                req.WriteNotImplementedResponse(
                    "<html><h1>HTTP method &quot;" + req.Method + "&quot; is not implemented</h1></html>");
                return;
            }

            // First get rid of formatting on requested file
            string toGet = req.URIDecoded;

            // Requested file/content name must never contain \ (only /)
            if (toGet.Contains("\\"))
            {
                m_logger.WriteLine(
                    "  URI contains \"\\\" -> sending 400 response");
                req.WriteBadRequestResponse();
                return;
            }

            m_logger.WriteLine("Got request for \"" + toGet + "\"");

            // Make sure the URL starts with the service URL, and prodived it does, strip
            // off that part.
            if (!toGet.StartsWith(ServiceURI))
            {
                req.WriteNotFoundResponse();
                return;
            }
            toGet = toGet.Substring(ServiceURI.Length);

            // Get the file or directory that we need to serve
            string[] parts = toGet.Split(
                new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
            FSDir dir = m_root, parent = null;

            for (int i = 0; i < parts.Length; i++)
            {
                parent = dir;
                dir    = dir.GetDir(parts[i]);

                // Should we get a null directory we have 2 cases:
                // 1. This is NOT the last loop iteration, implying that we've hit a not found.
                // 2. This IS the last loop iteration, meaning that the last part of the file
                //    string may be a file name and/or may be a not found. We fall through to
                //    the code after this loop in that case.
                if (null == dir && i != parts.Length - 1)
                {
                    if (null != m_onFileNotFound)
                    {
                        // Invoke custom handler
                        m_onFileNotFound(this, toGet, req);
                    }
                    else
                    {
                        req.WriteNotFoundResponse();
                    }
                    return;
                }
            }

            // If 'dir' is non-null at this point then it's a request for a listing of files in
            // that directory.
            if (null != dir)
            {
                m_logger.WriteLine("  Sending directory listing as response");

                // Build response and send it
                string response = m_onNeedDirList(dir);
                req.WriteHTMLResponse(response);

                // That's all we need for a directory listing
                return;
            }

            // Coming here implies a file name
            FSFile file = parent.GetFile(parts[parts.Length - 1]);

            // If it doesn't exist then we respond with a file-not-found response
            if (null == file)
            {
                if (null != m_onFileNotFound)                 // custom handler
                {
                    m_logger.WriteLine("  File not found -> invoking external handler");
                    m_onFileNotFound(this, parts[parts.Length - 1], req);
                }
                else
                {
                    m_logger.WriteLine("  File not found -> sending 404 response");

                    // If we don't have a handler for file-not-found then we use the generic 404
                    req.WriteNotFoundResponse();
                }
                return;
            }

            // File DOES exist, so we write the file data as the response
            m_logger.WriteLine("  Sending file contents as response");
            WriteFileResponse(req, file);
        }
Beispiel #14
0
        private void HandlePut(WebRequest req)
        {
            if (!m_allowUploads)
            {
                req.WriteBadRequestResponse();
                return;
            }

            // For now, don't allow overwriting
            if (GetEntry(req.URIDecoded) != null)
            {
                req.WriteBadRequestResponse();
                //req.WriteHTMLResponse("<html>Cannot overwrite files</html>");
                return;
            }

            // Make sure it starts with the service URI then remove that part
            string toPut = req.URIDecoded;

            if (!toPut.StartsWith(ServiceURI))
            {
                req.WriteBadRequestResponse();
                return;
            }
            toPut = toPut.Substring(ServiceURI.Length);

            // Parse the path and get the directories
            string[] pieces = toPut.Split('/');
            FSDir    dir    = m_root;

            for (int i = 0; i < pieces.Length - 1; i++)
            {
                dir = dir.GetDir(pieces[i]);
                if (dir == null)
                {
                    req.WriteBadRequestResponse();
                    return;
                }
            }

            // By the time we get here we expected the last piece of the URI to be the file name
            FSFile theUploadedFile = dir.CreateFile(pieces[pieces.Length - 1]);

            if (null == theUploadedFile)
            {
                // If we can't create the file at this point, we'll call it a bad request (because
                // it's most likely because of a bad file name)
                req.WriteBadRequestResponse();
                return;
            }

            // Stream in the body of request from the socket and out to the file
            Stream outS = theUploadedFile.OpenReadWrite();

            if (outS == null)
            {
                req.WriteInternalServerErrorResponse();
                return;
            }
            byte[] buf = new byte[8192];
            while (true)
            {
                int bytesRead = req.Body.Read(buf, 0, buf.Length);
                if (bytesRead <= 0)
                {
                    break;
                }
                outS.Write(buf, 0, bytesRead);
            }
            long fileLength = outS.Length;

            outS.Dispose();
            req.WriteHTMLResponse(string.Format("<html>OK: Uploaded {0} bytes</html>", fileLength));
        }