How to read back JPG with multiple data created in Unity? ( C# ) - c#

I'm really sorry if this has been asked before, or if it is obvious, but I'm not entirely sure how to progress here. Please excuse the length of the question.
For a research project, my intention is to create a JPG image that contains after the JPG data a small plain text and another image as well. This image would be used later as an AR marker that 'contains' all the data to be displayed. I read that this could also be achieved with Stenography, but encrypting is not necessarily the goal, I think.
So far, using various other methods found here, the script can create a readable image that adds the byte[] data of the other content right after the first JPG, but I'm not entirely sure how to read it back.
using UnityEngine;
using Crosstales.FB;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using System;
using System.Text;
public class JPGMan : MonoBehaviour {
public UnityEngine.UI.Image markerThumb;
public UnityEngine.UI.Image mediaThumb;
public UnityEngine.UI.InputField descF;
Encoding u8 = Encoding.UTF8;
public void initLoadMarker()
{
string markerExt = "";
string markerPath = FileBrowser.OpenSingleFile("Image for Marker", "", markerExt);
StartCoroutine(loadMarker(markerPath));
Debug.Log(markerPath);
}
public void initLoadMedia()
{
string mediaExt = "";
string mediaPath = FileBrowser.OpenSingleFile("Image for Media", "", mediaExt);
StartCoroutine(loadMedia(mediaPath));
Debug.Log(mediaPath);
}
IEnumerator loadMarker(string mPath)
{
WWW markerWWW;
using (markerWWW = new WWW("File:///" + mPath))
{
yield return markerWWW;
Debug.Log(markerWWW.isDone);
Rect protoRect = new Rect(Vector2.zero, new Vector2(markerWWW.texture.width, markerWWW.texture.height));
Sprite protoSprite = Sprite.Create(markerWWW.texture, protoRect, Vector2.zero);
markerThumb.sprite = protoSprite;
markerThumb.preserveAspect = true;
}
}
IEnumerator loadMedia(string mPath)
{
WWW mediaWWW;
using (mediaWWW = new WWW("File:///" + mPath))
{
yield return mediaWWW;
Debug.Log(mediaWWW.isDone);
Rect protoRect = new Rect(Vector2.zero, new Vector2(mediaWWW.texture.width, mediaWWW.texture.height));
Sprite protoSprite = Sprite.Create(mediaWWW.texture, protoRect, Vector2.zero);
mediaThumb.sprite = protoSprite;
mediaThumb.preserveAspect = true;
}
}
public void encodeJPG()
{
string finalExt = "jpg";
string finalPath = FileBrowser.SaveFile("Save Marker", "", "aMARKER", finalExt);
Texture2D protoJPG = new Texture2D((int)mediaThumb.sprite.textureRect.width, (int)mediaThumb.sprite.textureRect.height);
Color[] pixels = mediaThumb.sprite.texture.GetPixels((int)mediaThumb.sprite.textureRect.x, (int)mediaThumb.sprite.textureRect.y, (int)mediaThumb.sprite.textureRect.width, (int)mediaThumb.sprite.textureRect.height);
protoJPG.SetPixels(pixels);
protoJPG.Apply();
Texture2D protoMarker = new Texture2D((int)markerThumb.sprite.textureRect.width, (int)markerThumb.sprite.textureRect.height);
Color[] markerPixels = markerThumb.sprite.texture.GetPixels((int)markerThumb.sprite.textureRect.x, (int)markerThumb.sprite.textureRect.y, (int)markerThumb.sprite.textureRect.width, (int)markerThumb.sprite.textureRect.height);
protoMarker.SetPixels(markerPixels);
protoMarker.Apply();
byte[] markerBytes = protoMarker.EncodeToJPG(100);
byte[] imgBytes = protoJPG.EncodeToJPG(100);
byte[] textBytes = System.Text.Encoding.Unicode.GetBytes(descF.text);
byte[] finalBytes = new byte[markerBytes.Length + textBytes.Length + imgBytes.Length];
System.Buffer.BlockCopy(markerBytes, 0, finalBytes, 0, markerBytes.Length);
System.Buffer.BlockCopy(textBytes, 0, finalBytes, markerBytes.Length, textBytes.Length);
System.Buffer.BlockCopy(imgBytes, 0, finalBytes, markerBytes.Length + textBytes.Length, imgBytes.Length);
File.WriteAllBytes(finalPath, finalBytes);
}
public void decodeJPG()
{
string inExt = "jpg";
string inPath = FileBrowser.OpenSingleFile("Open Marker", "", inExt);
StartCoroutine(loadMarkerImage(inPath));
}
IEnumerator loadMarkerImage(string inPath_)
{
byte[] bytesFile;
char[] lookFiles = new char[] {'J','F','I','F'};
using (FileStream fs = File.OpenRead(inPath_))
{
yield return fs;
bytesFile = new byte[20];
fs.Read(bytesFile, 0, 20);
fs.Close();
}
byte[] searchBytes = u8.GetBytes(lookFiles);
List<int> rList = IndexOfSequence(bytesFile, searchBytes, 0);
Debug.Log(rList[0].ToString());
}
I'm thinking that I could maybe find a way to write to the JPG spec's APP0 marker, but I haven't found much about doing this specifically in Unity. Another could be to loop through the whole byte[] to find the headers and thus differentiate the data, although I read that reading all the bytes in one operation is not particularly fast.
Finally, I was thinking of appending the serialization of a class that contains the extra data, making it possible to deserialize it and just refer to it as properties of the class. But that's probably a bad idea.
I am aware this is very unusual, and I don't mind as well just using a regular database and link the images to that data.
Is there anyone that could help with this matter? Any help is appreciated.
Thank you all for your time.

Related

How to get an image from a url and set it to an object in Unity

I have a method to create objects from prefabs. The prefab consists of the texts "Title" and "Description", as well as the image "Image".
In the method below, I get the text for "Title", "Description" and the URL of the image for "Image".
using System.Collections;
using UnityEngine;
using UnityEngine.UI;
using Newtonsoft.Json.Linq;
using System.Threading.Tasks;
using UnityEngine.Networking;
public class CreateMonumentButtons : MonoBehaviour
{
public RectTransform prefarb;
public RectTransform content;
private string City;
private string URL = DataHolders.URL;
async private void Awake()
{
City = DataHolders.City;
StartCoroutine(Get());
}
void InitializeItemView (GameObject viewGameObject, string TextTitle, string TextDescription, string URLImg)
{
TestItemView view = new TestItemView(viewGameObject.transform);
view.Title.text = TextTitle;
view.Description.text = TextDescription;
File fileData;
Texture2d txture;
(File.Exists(URLImg)) {
fileData = File.ReadAllBytes(URLImg); //read the bytes from the file at URLImg's destination
txture = new Texture2D(0,0);//makes the new texture
txture.LoadImage(fileData);//loads the image from the file
view.Img.sprite = Sprite.Create(txture, new Rect(0,0,200,200), new Vector2());
//set Img.sprite to a new sprite at 0,0 with the dimensions 200x200
}
}
public class TestItemView
{
public Text Title;
public Text Description;
// public Image Image;
public TestItemView (Transform rootView)
{
Title = rootView.Find("Title").GetComponent<Text>();
Description = rootView.Find("Description").GetComponent<Text>();
// Image = rootView.Find("Image").GetComponent<Image>();
}
}
private IEnumerator Get()
{
WWWForm formT = new WWWForm();
formT.AddField("City", City);
WWW wwwT = new WWW(URL + "get_monuments.php", formT);
yield return wwwT;
if (wwwT.error != null)
{
Debug.Log("Ошибка: " + wwwT.error);
yield break;
}
WWWForm formD = new WWWForm();
formD.AddField("City", City);
WWW wwwD = new WWW(URL + "get_description.php", formD);
yield return wwwD;
if (wwwD.error != null)
{
Debug.Log("Ошибка: " + wwwD.error);
yield break;
}
WWWForm formI = new WWWForm();
formI.AddField("City", City);
WWW wwwI = new WWW(URL + "get_img.php", formI);
yield return wwwI;
if (wwwI.error != null)
{
Debug.Log("Ошибка: " + wwwI.error);
yield break;
}
WWWForm formL = new WWWForm();
formL.AddField("City", City);
WWW wwwL = new WWW(URL + "get_lenLists.php", formL);
yield return wwwL;
if (wwwL.error != null)
{
Debug.Log("Ошибка: " + wwwL.error);
yield break;
}
DataHolders.listLen = int.Parse(wwwL.text);
DataHolders.listImg = wwwI.text;
DataHolders.listDescription = wwwD.text;
DataHolders.listMonuments = wwwT.text;
var ListTitls = JObject.Parse(DataHolders.listMonuments);
var ListDescriptions = JObject.Parse(DataHolders.listDescription);
var ListImgs = JObject.Parse(DataHolders.listImg);
for( int i = 0; i < DataHolders.listLen; i++ )
{
// Debug.Log(i.ToString());
var Title = (string) ListTitls[i.ToString()];
var Description = (string) ListDescriptions[i.ToString()];
var Img = (string) ListImgs[i.ToString()];
var instance = GameObject.Instantiate(prefarb.gameObject) as GameObject;
instance.transform.SetParent(content, false);
InitializeItemView(instance, Title, Description, Img);
}
}
}
I don't understand how can I get an image from an image url and set its value to "Image".
But I am a beginner, so it is desirable that you explain something in simple terms.
Unity Beginner has explained it easily in just 1.5 minutes to Load Image from WebLink or URL.
But here, they used WWW, which is obsolete, so use UnityWebRequest instead. Here are the changes you need to perform while changing from WWW to UnityWebRequest.
Image doesn't have an attribute of Text, and instead you should get the file, load the image to a texture and set the Image's sprite as the texture.
File fileData;
Texture2d txture;
if (File.Exists(URLImg)) {
fileData = File.ReadAllBytes(URLImg); //read the bytes from the file at URLImg's destination
txture = new Texture2D(0,0);//makes the new texture
txture.LoadImage(fileData);//loads the image from the file
view.Img.sprite = Sprite.Create(txture, new Rect(0,0,200,200), new Vector2());
//set Img.sprite to a new sprite at 0,0 with the dimensions 200x200
}
with view.Img.sprite as an image rather than "text" because it's not painting text and rather an image.
I made a simple plugin for this.
If someone is interested, check this repo

Save an Image with superimposed Label on the device

I have some images in my ftp web space, and through this method I can view them on the screen, and above them I insert a Label.
var ImageUrl = "ftp://xxxxxxxxx.jpg";
//Download Image
byte[] ImgByte1 = WebClient.DownloadData(ImageUrl);
MemoryStream mStream1 = new MemoryStream(ImgByte1);
ObservableCollection<FraseClass> listFrasi = new ObservableCollection<FraseClass>
{
new FraseClass{Source=ImageSource.FromStream(() => mStream1)},
}
XAML
<Image
Source="{Binding Source}"/>
<Label
Text="Hello"/>
I am looking for a way to be able to save the image on the device with the text superimposed. I tried to search but couldn't find anything to fix my problem
I am looking for a way to be able to save the image on the device with the text superimposed. I tried to search but couldn't find anything to fix my problem
According to your description, you want to save image that downloadind from url into local storage, am I right?
If yes, I suggest you can use Xamarin.Forms DependencyService to do this.
Firstly, create an interface in shared code.
public interface IImageFile
{
void SaveImage(string name, byte[] data, string location = "temp");
}
Then implement the interface on Android platform. Don't forget to register the platform implementations.
[assembly: Dependency(typeof(ImageFile))]
namespace FormsSample.Droid
{
public class ImageFile : IImageFile
{
public void SaveImage(string name, byte[] data, string location = "temp")
{
var documentsPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
documentsPath = System.IO.Path.Combine(documentsPath, "Orders", location);
Directory.CreateDirectory(documentsPath);
string filePath = System.IO.Path.Combine(documentsPath, name);
using (FileStream fs = new FileStream(filePath, FileMode.OpenOrCreate))
{
int length = data.Length;
fs.Write(data, 0, length);
}
}
}
}
Finally, resolving platform implementations with the DependencyService
private void btn1_Clicked(object sender, EventArgs e)
{
var ImageUrl = "ftp://xxxxxxxxx.jpg";
//Download Image
byte[] ImgByte1 = WebClient.DownloadData(ImageUrl);
DependencyService.Get<IImageFile>().SaveImage("ImageName.jpg", ImgByte1, "imagesFolder");
}
you need to add permission WRITE_EXTERNAL_STORAGE and READ_EXTERNAL_STORAGE in AndroidMainfeast.xml, then you also need to Runtime Permission Checks in Android 6.0. Using the following code in Mainactivity.cs to request permissions.
private void checkpermission()
{
if (ContextCompat.CheckSelfPermission(this, Manifest.Permission.WriteExternalStorage) == (int)Permission.Granted)
{
// We have permission, go ahead and use the writeexternalstorage.
}
else
{
// writeexternalstorage permission is not granted. If necessary display rationale & request.
}
if (ContextCompat.CheckSelfPermission(this, Manifest.Permission.ReadExternalStorage) == (int)Permission.Granted)
{
// We have permission, go ahead and use the ReadExternalStorage.
}
else
{
// ReadExternalStorage permission is not granted. If necessary display rationale & request.
}
}
Detailed info about IOS platform, you can take a look:
How to download image and save it in local storage using Xamarin-Forms.?
Update:
If you want to add text watermark on image, you need to convert byte[] to bitmap to add text firstly.
Android.Graphics.Bitmap mutableBitmap;
Android.Graphics.Bitmap bitmap;
public void ConvertImage(byte[] imageArray)
{
bitmap = BitmapFactory.DecodeByteArray(imageArray, 0, imageArray.Length);
}
public void Watermark()
{
mutableBitmap = bitmap.Copy(Android.Graphics.Bitmap.Config.Argb8888, true);
Canvas canvas = new Canvas(mutableBitmap);
// Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint();
paint.Color = Android.Graphics.Color.Black;
paint.TextSize = 50;
canvas.DrawText("hellod world", 50, 50, paint);
}
Then convert bitmap into byte[] to save image into local storage.
public void convertbitmap(Bitmap bitmap)
{
bitmap = mutableBitmap;
MemoryStream stream = new MemoryStream();
bitmap.Compress(Bitmap.CompressFormat.Png, 0, stream);
byte[] bitmapData = stream.ToArray();
}

How to pass xml file inside of code instead of a folder path?

I'am trying to not create a file, and pass xml document straight to a SkiaSharp method Load. I mean, is there the way to imitate a path? So here is the code:
public IActionResult svgToPng(string itemId, string mode = "
{
var svgSrc = new XmlDocument();
svgSrc.LoadXml(/*Some xml code*/);
string svgSaveAs = "save file path";
var quality = 100;
var svg = new SkiaSharp.Extended.Svg.SKSvg();
var pict = svg.Load(svgSrc); // HERE, it needs to be a path, not XmlDocument, but i want to pass straight
var dimen = new SkiaSharp.SKSizeI
(
(int) Math.Ceiling(pict.CullRect.Width),
(int) Math.Ceiling(pict.CullRect.Height)
);
var matrix = SKMatrix.MakeScale(1, 1);
var img = SKImage.FromPicture(pict, dimen, matrix);
// Convert to PNG
var skdata = img.Encode(SkiaSharp.SKEncodedImageFormat.Png, quality);
using(var stream = System.IO.File.OpenWrite(svgSaveAs))
{
skdata.SaveTo(stream);
}
ViewData["Content"] = "PNG file was created out of SVG.";
return View();
}
The Load method seems to be this:
public SKPicture Load(
using (var stream = File.OpenRead(filename))
{
return Load(stream);
}
}
look at the code of that library :
https://github.com/mono/SkiaSharp.Extended/blob/master/SkiaSharp.Extended.Svg/source/SkiaSharp.Extended.Svg.Shared/SKSvg.cs
Look at the Load method, it has multiple implementations :
public SKPicture Load(string filename)
{
using (var stream = File.OpenRead(filename))
{
return Load(stream);
}
}
public SKPicture Load(Stream stream)
{
using (var reader = XmlReader.Create(stream, xmlReaderSettings, CreateSvgXmlContext()))
{
return Load(reader);
}
}
public SKPicture Load(XmlReader reader)
{
return Load(XDocument.Load(reader));
}
You will need to pick one of them and use it. Now, nothing stops you from getting the code and adding one extra Load for an XML string for example, but since this is a library you do not control, I'd stick to what you are given.
You could use the XmlReader version, that's probably the closest one to what you want.

Unity Resources.load() won't work with external dll

I'm trying to load an asset using Resources.load() but it always returns null.
Here is my folder structure: https://imgur.com/a/z8KObW1
When I use Resources.load() in my unity project, it works without any problem.
But when I create a seperate project in visual studio with the unityengine dll's, and use Resources.load() in one of the .cs files under the Source folder it always seems to return null, no matter what I try.
When I placed the Assets folder inside of the Source folder it returned null and when I placed the .cs files in the Asset folder it also returned null. I can't seem to figure out why. I also tried to fetch the path that the Resources.load() starts at, but I can't seem to figure it out. Does it start from the .dll or from the .cs file under the Source?
public static void Create_Manager() {
GameObject networkManagerObj = new GameObject();
networkManagerObj.name = "_NetworkManager";
networkManager = networkManagerObj.AddComponent<NetworkManager>();
networkManager.playerPrefab = Resources.Load("Player") as GameObject;
networkManager.dontDestroyOnLoad = true;
networkManager.runInBackground = true;
networkManager.autoCreatePlayer = true;
}
All I need is for the Resources.load() to work with my seperate project/dll. Does anyone know how to do so?
Here's the code that a coworker of mine wrote and mostly explained to me. I've omitted a lot of the code that dealt with what this class was for (handling touch input), leaving only the sections related to loading an image (used to display a touch point on screen).
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using UnityEngine;
using HedgehogTeam.EasyTouch;
namespace TouchControls
{
/// <summary>Helper to control touch-based input.</summary>
public static class Utility
{
const string imageNamespace = "TouchControls.";
const string imageResourceFolder = "TouchControls/Images/";
/// <summary>Static helper to contain texture resources.</summary>
public static class Texture
{
/// <summary>The texture used to represent a second finger when simulating touches.</summary>
public static Texture2D SecondFinger
{
get
{
if (null == secondFinger)
secondFinger = LoadImage(secondFingerFilename);
return secondFinger;
}
}
static Texture2D secondFinger = null;
const string secondFingerFilename = "secondFinger.png";
}
static Assembly ExecutingAssembly
{
get
{
if (null == executingAssembly)
{
executingAssembly = Assembly.GetExecutingAssembly();
}
return executingAssembly;
}
}
static Assembly executingAssembly = null;
static Stream GetImageStream(string imageName) { return ExecutingAssembly.GetManifestResourceStream(imageName); }
static Texture2D LoadImage(string filename, TextureWrapMode wrapMode = TextureWrapMode.Clamp, FilterMode filterMode = FilterMode.Bilinear, bool useMipMaps = true, TextureFormat format = TextureFormat.ARGB32)
{
Texture2D texture = Resources.Load<Texture2D>(imageResourceFolder + Path.GetFileNameWithoutExtension(!string.IsNullOrEmpty(filename) ? filename : string.Empty));
try
{
// Didn't find it in resources in the project so try to find it in the library manifest....
if (null == texture)
{
using (Stream stream = GetImageStream(imageNamespace + filename))
{
texture = new Texture2D(0, 0, format, useMipMaps);
if (!texture.LoadImage(GetImageBuffer(stream)))
throw new NotSupportedException(filename);
texture.wrapMode = wrapMode;
texture.filterMode = filterMode;
}
}
else // ensure it is read/write enabled...
{
Texture2D invertedTexture = new Texture2D(texture.width, texture.height, texture.format, 1 < texture.mipmapCount);
invertedTexture.SetPixels32(texture.GetPixels32());
invertedTexture.Apply(true);
texture = invertedTexture;
}
}
catch
{
// Something went wrong so make a magenta 4 pixel texture.
texture = new Texture2D(2, 2, TextureFormat.ARGB32, false);
texture.SetPixels(0, 0, 2, 2, Enumerable.Repeat(Color.magenta, 4).ToArray());
}
texture.Apply(true);
return texture;
}
static byte[] GetImageBuffer(Stream stream)
{
stream.Seek(0, SeekOrigin.Begin);
byte[] buffer = new byte[stream.Length];
stream.Read(buffer, 0, (int)stream.Length);
return buffer;
}
}
}
It first attempts to load an image out of the Resources folder (as you're familiar with) at a given location: this allows the image to be overridden by the local project, if desired. If that image isn't found, then it loads one out of the DLL. I am not sure where this image is located, but as the path reference is in the code, I'm sure it works out sensibly enough (the key part he had informed me was how to insure that the file got included in the DLL, rather than where it was located).
The important chunk for loading it out of the DLL is this section:
static Stream GetImageStream(string imageName) { return ExecutingAssembly.GetManifestResourceStream(imageName); }
//...
using (Stream stream = GetImageStream(imageNamespace + filename))
{
texture = new Texture2D(0, 0, format, useMipMaps);
if (!texture.LoadImage(GetImageBuffer(stream)))
throw new NotSupportedException(filename);
texture.wrapMode = wrapMode;
texture.filterMode = filterMode;
}
//...
texture.Apply(true);
return texture;

Pack files into one, to later programmatically unpack them [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking us to recommend or find a tool, library or favorite off-site resource are off-topic for Stack Overflow as they tend to attract opinionated answers and spam. Instead, describe the problem and what has been done so far to solve it.
Closed 9 years ago.
Improve this question
Is it possible to take all files and folders in a directory and pack them into a single package file, so that I may transfer this package over network and then unpack all files and folders from the package?
I tried looking into ZIP files with C#, because I'm aiming for the same idea, but the actual methods for it only comes with .NET 3.5 (I believe), I also want the program to be very lightweight, meaning I don't want external modules lying around that has to be taken with if I wish to unzip/unpack a single file.
How can I accomplish this?
Just use a BinaryWriter/Reader and your own format. Something like this:
using (var fs = File.Create(...))
using (var bw = new BinaryWriter(fs))
{
foreach (var file in Directory.GetFiles(...))
{
bw.Write(true); // means that a file will follow
bw.Write(Path.GetFileName(file));
var data = File.ReadAllBytes(file);
bw.Write(data.Length);
bw.Write(data);
}
bw.Write(false); // means end of file
}
So basically you write a bool that means whether there is a next file, the name and contents of each file, one after the other. Reading is the exact opposite. BinaryWriter/Reader take care of everything (it knows how long each string and byte array is, you will read back exactly what you wrote).
What this solution lacks: not an industry standard (but quite simple), doesn't store any additional metadata (you can add creation time, etc.), doesn't use a checksum (you can add an SHA1 hash after the contents), doesn't use compression (you said you don't need it), doesn't handle big files well (the problematic part is that it reads an entire file into a byte array and writes that, should work pretty well under 100 MB), doesn't handle multi-level directory hierarchies (can be added of course).
EDIT: The BinaryR/W know about string lengths, but not about byte array lengths. I added a length field before the byte array so that it can be read back exactly as it was written.
Take a look at ziplib, it's free, open source and can be used in all .NET versions: http://www.icsharpcode.net/opensource/sharpziplib/
What i suggest you it's to consider the advantages taken by using an external library so you can forget a lot of troubles. A zip complex class could be a huge deal. Take a look at that: http://dotnetzip.codeplex.com/ it's simple, stable and lightweigth.
By the way if you totally don'want external libraries and data compression isn't mandatory for your project you can manage it in a sort like this (please, consider it as a sample written in lees than an hour ;-) ):
usage:
//to pack
Packer.SimplePack sp = new Packer.SimplePack(#"c:\filename.pack");
sp.PackFolderContent(#"c:\yourfolder");
sp.Save();
//to unpack
Packer.SimplePack sp = new Packer.SimplePack(#"c:\filename.pack");
sp.Open();
Here is SimplePack:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Packer
{
public class SimplePack
{
public class Header
{
public Int32 TotalEntries { get; set; }
public Int64[] EntriesSize
{
get
{
return EntriesSizeList.ToArray();
}
}
private List<Int64> EntriesSizeList { get; set; }
public Header()
{
TotalEntries = 0;
EntriesSizeList = new List<Int64>();
}
public void AddEntrySize(Int64 newSize)
{
EntriesSizeList.Add(newSize);
}
}
public class Item
{
public Byte[] RawData { get; set; }
public String Name { get; set; }
public String RelativeUri { get; set; }
public Int64 ItemSize
{
get
{
Int64 retVal = 4; //Name.Lenght;
retVal += Name.Length;
retVal += 4; //RelativeUri.Length
retVal += RelativeUri.Length;
retVal += RawData.Length;
return retVal;
}
}
public Byte[] SerializedData
{
get
{
List<Byte> retVal = new List<Byte>();
retVal.AddRange(BitConverter.GetBytes(Name.Length));
retVal.AddRange(Encoding.Default.GetBytes(Name));
retVal.AddRange(BitConverter.GetBytes(RelativeUri.Length));
retVal.AddRange(Encoding.Default.GetBytes(RelativeUri));
retVal.AddRange(RawData);
return retVal.ToArray();
}
}
public Item()
{
RawData = new Byte[0];
Name = String.Empty;
RelativeUri = String.Empty;
}
public Item(Byte[] serializedItem)
{
Int32 cursor = 0;
Int32 nl = BitConverter.ToInt32(serializedItem, cursor);
cursor += 4;
Name = Encoding.Default.GetString(serializedItem, cursor, nl);
cursor += nl;
Int32 rl = BitConverter.ToInt32(serializedItem, cursor);
cursor += 4;
RelativeUri = Encoding.Default.GetString(serializedItem, cursor, rl);
cursor += rl;
RawData = new Byte[serializedItem.Length - cursor];
for (int i = cursor; i < serializedItem.Length; i++)
{
RawData[i - cursor] = serializedItem[cursor];
}
}
}
public FileInfo PackedFile { get; private set; }
public List<Item> Data { get; private set; }
public Header FileHeaderDefinition { get; private set; }
public SimplePack(String fileName)
{
PackedFile = new FileInfo(fileName);
FileHeaderDefinition = new Header();
Data = new List<Item>();
}
public Boolean PackFolderContent(String folderFullName)
{
Boolean retVal = false;
DirectoryInfo di = new DirectoryInfo(folderFullName);
//Think about setting up strong checks and errors trapping
if (di.Exists)
{
FileInfo[] files = di.GetFiles("*", SearchOption.AllDirectories);
foreach (FileInfo fi in files)
{
Item it = setItem(fi, di.FullName);
if (it != null)
{
Data.Add(it);
FileHeaderDefinition.TotalEntries++;
FileHeaderDefinition.AddEntrySize(it.ItemSize);
}
}
}
//althoug it isn't checked
retVal = true;
return retVal;
}
private Item setItem(FileInfo sourceFile, String packedRoot)
{
if (sourceFile.Exists)
{
Item retVal = new Item();
retVal.Name = sourceFile.Name;
retVal.RelativeUri = sourceFile.FullName.Substring(packedRoot.Length).Replace("\\", "/");
retVal.RawData = File.ReadAllBytes(sourceFile.FullName);
return retVal;
}
else
{
return null;
}
}
public void Save()
{
if (PackedFile.Exists)
{
PackedFile.Delete();
System.Threading.Thread.Sleep(100);
}
using (FileStream fs = new FileStream(PackedFile.FullName, FileMode.CreateNew, FileAccess.Write))
{
//Writing Header
//4 bytes
fs.Write(BitConverter.GetBytes(FileHeaderDefinition.TotalEntries), 0, 4);
//8 bytes foreach size
foreach (Int64 size in FileHeaderDefinition.EntriesSize)
{
fs.Write(BitConverter.GetBytes(size), 0, 8);
}
foreach (Item it in Data)
{
fs.Write(it.SerializedData, 0, it.SerializedData.Length);
}
fs.Close();
}
}
public void Open()
{
if (PackedFile.Exists)
{
using (FileStream fs = new FileStream(PackedFile.FullName, FileMode.Open, FileAccess.Read))
{
Byte[] readBuffer = new Byte[4];
fs.Read(readBuffer, 0, readBuffer.Length);
FileHeaderDefinition.TotalEntries = BitConverter.ToInt32(readBuffer, 0);
for (Int32 i = 0; i < FileHeaderDefinition.TotalEntries; i++)
{
readBuffer = new Byte[8];
fs.Read(readBuffer, 0, readBuffer.Length);
FileHeaderDefinition.AddEntrySize(BitConverter.ToInt64(readBuffer, 0));
}
foreach (Int64 size in FileHeaderDefinition.EntriesSize)
{
readBuffer = new Byte[size];
fs.Read(readBuffer, 0, readBuffer.Length);
Data.Add(new Item(readBuffer));
}
fs.Close();
}
}
}
}
}

Categories