/// <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); }
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); }
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; } }
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; }
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])); }
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); }
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); }
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); } }
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!"); }
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); }
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()); }
/// <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 "" + req.Method + "" 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); }
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)); }