0

MS VS 2010, XNA 4.0
So, I have a class Planet & it has Save and Load functions.

public void SaveToFile(ContentManager content)
    {
        string path = content.RootDirectory + @"\Objects\Planets\" + this.name +@".planet"; 
        using (BinaryWriter bw = new BinaryWriter(File.Open(path, FileMode.Create)))
        {
            bw.Write(name);

            //sprite names
            bw.Write(planet.Sprite.Name); //"planet" is an animation.
            bw.Write(planet.HorFrames);   //and thats why it has a sprite
            bw.Write(planet.VerFrames);   //that has a name
            bw.Write((double)planet.FPS);

            bw.Write(asteroid1.Name);
            bw.Write(asteroid2.Name);
            bw.Write(asteroid3.Name);
            bw.Write(backgroundA.Name);

            //update related
            bw.Write((double)RSpeed);
            bw.Write((double)ASVariety);
            bw.Write((double)A1Chance);
            bw.Write((double)A2Chance);
            bw.Write((double)A3Chance);
            bw.Close();
        }
    }

public void LoadFromFile(ContentManager content, string name)
    {
        string path = content.RootDirectory + @"\Objects\Planets\" + name + @".planet";
        using (BinaryReader br = new BinaryReader(File.OpenRead(path)))
        {
            this.name = br.ReadString();
            this.planet = new Animation(Cont.Texture2D(br.ReadString()), br.ReadInt32(), br.ReadInt32(), (float)br.ReadDecimal(), true);
            this.asteroid1 = Cont.Texture2D(br.ReadString());
            this.asteroid2 = Cont.Texture2D(br.ReadString());
            this.asteroid3 = Cont.Texture2D(br.ReadString());
            this.backgroundA = Cont.Texture2D(br.ReadString());
            this.RSpeed = (float)br.ReadDouble();
            this.ASVariety = (float)br.ReadDouble();
            this.A1Chance = (float)br.ReadDouble();
            this.A2Chance = (float)br.ReadDouble();
            this.A3Chance = (float)br.ReadDouble();

            bposB = new Vector2(backgroundA.Width - 1, 0);
            bspd = new Vector2(-RSpeed / 8f, 0);

            pEngine1 = new ParticleEngine(MSTime, 40, new Vector2(20, -30), new Vector2(2, 1), asteroid1, new Color(100, 100, 100));
            pEngine1.Follow(new Vector2(-200, Game1.window_height / 2));

            pEngine2 = new ParticleEngine(MSTime * 3, 40, new Vector2(20, -30), new Vector2(2, 1), asteroid2, new Color(100, 100, 100));
            pEngine2.Follow(new Vector2(-200, Game1.window_height / 2));

            br.Close();
        }

Thats simple to understand, now isn't it?
Now, when I try to load sprite for planet animation, I get an error, that says:
"Value cannot be null.
Parameter name: assetName"
and this is happening at the line
this.planet = new Animation(Cont.Texture2D(br.ReadString()), br.ReadInt32(), br.ReadInt32(), (float)br.ReadDecimal(), true);
Cont.Texture2D is a static functions that returns a Texture2D witch name is equal to the path it was loaded from, and it looks like this:

public static Texture2D Texture2D(string path)
    {
        Texture2D sprite = content.Load<Texture2D>(path);
        sprite.Name = path;
        return sprite;
    }

So, when I'm saving it, it saves the right path. The test function looks like this:

private void testFunction()
    {
        /*
        p = new Planet("Mars", 
            new Animation(Cont.Texture2D("Sprites\\Planets\\mars"), 8, 2, 0.9f, true),
            Cont.Texture2D("Sprites\\Backgrounds\\space"),
            Cont.Texture2D("Sprites\\Asteroids\\small\\small 1"),
            Cont.Texture2D("Sprites\\Asteroids\\medium\\medium 1"),
            Cont.Texture2D("Sprites\\Asteroids\\big\\big 1"),
            100, 50, 40, 20, 40, 40);
        p.SaveToFile(Content);
        */
        p = new Planet(Content, "Mars");
        tests = p.ToString();
    }

"tests" is just a test string. Now, I first execute the commented code, so it could save the file, and then i execute the uncommented code. This planet constructor just calls the LoadFromFile function and nothing more. Also, I have the saved file in my content in my project. I did say for that file to treat it as content (dont compile it). So, the code "sees" the file, thats for shure, but it cant find the .png on the path that was read from the mars.planet. Is there maybe a misstake if i store 2 strings one after another, and then the reader cant see where is the end of the first one? Am I maybe saving it wrong?

My goal is to have binary files that will be loaded and saved, and in them would be planets, rockets, maps, etc, that are collections of names and values that are needed for constructors of these classes.

Monset
  • 648
  • 5
  • 25
  • 1
    You write `planet.FPS` as `double` but you try to read it as a `decimal`? – cbr Jun 20 '15 at 23:57
  • The error message is telling you that there is a parameter named `assetName`, the value of which is `null` when a non-null value is required. For sure, this is not directly related to `BinaryReader`. As for what it could be, that's impossible to say unless you provide [a good, _minimal_, _complete_ code example](http://stackoverflow.com/help/mcve) that reliably reproduces the problem. – Peter Duniho Jun 21 '15 at 01:25
  • FPS is a float. Binary writer-reader doesn't have a float, so when I save a float, i save it as decimal, and when i load it, i load the decimal and turn it in float: (float)br.ReadDecimal();. – Monset Jun 21 '15 at 12:13
  • @PeterDuniho : I don't have any more code that would help you understand my issue. I made "Cont" a static class that can load textures/sprites/music... and i loaded it before running the test function, so that means that it runs propertly. i also tested it on other sprite paths and it works. After preparing the Cont class, i called test function. I don't see how I can make this more good, minimal, complete code example that reliably reproduces the problem. – Monset Jun 21 '15 at 12:24

2 Answers2

1

Due to unnessesary complications with Binary writer-reader, I have decided to use Stream writer-reader. Big thanks to @Blas Soriano & @Peter Duniho.

Binary writers seem not to be able to write 2 strings one after another in such way that binary reader can read them, at least not in my case. I did try writing and reading 2 strings with binary w-r in another project, and they seem to work fine there. I hope nobody expiriences this kind of thing that happened to me. Thanks again to Blas and Peter.

EDIT:
It seems that my file that I included in Content of solution, "Mars.planet", in it's properties: Copy To Output, from 3 possible choices: Copy if newer, Never Copy, Always Copy, had selected the Copy Always, and that didnt allow changes to happen to that file, so, i DID change the file by saving it with my function, but, then I exit the game, so the file goes back to the old version (not really shure how), and that version is a realy old one, that didn't have all lines that I am in need now, so when I comment the saving code and uncomment the loading code, start the game, the function for loading would actually be loading the first version of the file (the version from the time when I included the file).

It works now, but not because I am using the Stream w-r, but because I changed the property of that file to:Do not copy. I will be going back to Binary w-r.

Monset
  • 648
  • 5
  • 25
0

In your testFunction() you create a new Planet and save it to a file without assigning the sprite name (and I guess it is not assigned in the Animation constructor), so when it is saved the sprite name will be null. To avoid that, you need to assign the planet.Sprite.Name before saving it to a file.

private void testFunction()
{
    Animation planetAnimation = new Animation(Cont.Texture2D("Sprites\\Planets\\mars"));
    planetAnimation.Sprite.Name = "Sprites\\Planets\\mars";
    p = new Planet("Mars", 
        planetAnimation , 8, 2, 0.9f, true),
        Cont.Texture2D("Sprites\\Backgrounds\\space"),
        Cont.Texture2D("Sprites\\Asteroids\\small\\small 1"),
        Cont.Texture2D("Sprites\\Asteroids\\medium\\medium 1"),
        Cont.Texture2D("Sprites\\Asteroids\\big\\big 1"),
        100, 50, 40, 20, 40, 40);
    p.SaveToFile(Content);
    /*
    p = new Planet(Content, "Mars");
    tests = p.ToString();
    */
}

Instead of that, you could modify your Load and Save methods to avoid saving twice that string. First delete or comment the line bw.Write(planet.Sprite.Name) in SaveToFile():

public void SaveToFile(ContentManager content)
{
    string path = content.RootDirectory + @"\Objects\Planets\" + this.name +@".planet"; 
    using (BinaryWriter bw = new BinaryWriter(File.Open(path, FileMode.Create)))
    {
        bw.Write(name);

        //sprite names
        //bw.Write(planet.Sprite.Name); //"planet" is an animation.
        bw.Write(planet.HorFrames);   //and thats why it has a sprite
        bw.Write(planet.VerFrames);   //that has a name
        bw.Write((double)planet.FPS);
...

Then change the LoadFromFile() to reuse the string:

public void LoadFromFile(ContentManager content, string name)
{
    string path = content.RootDirectory + @"\Objects\Planets\" + name + @".planet";
    using (BinaryReader br = new BinaryReader(File.OpenRead(path)))
    {
        this.name = br.ReadString();
        this.planet = new Animation(Cont.Texture2D(this.name), br.ReadInt32(), br.ReadInt32(), br.ReadDouble(), true);
        this.asteroid1 = Cont.Texture2D(br.ReadString());
        this.asteroid2 = Cont.Texture2D(br.ReadString());

Note that following cubrr suggestion, I replaced decimal to double while reading planet.FPS.


EDIT: From your comment it seems you think the null string should be assigned by using the static method, but there are 2 different things here.
First take a look at that name inside Cont.Texture2D():

public static Texture2D Texture2D(string path)
{
    Texture2D sprite = content.Load<Texture2D>(path);
    sprite.Name = path;
    return sprite;
}

This name is Texture2D.Name
Now take a look inside SaveToFile():

bw.Write(name);

//sprite names
bw.Write(planet.Sprite.Name); //"planet" is an animation.

Here we can see 2 different names, Planet.Name and Planet.Sprite.Name. Where are you assigning that second name? That's why I used planetAnimation.Sprite.Name = "Sprites\\Planets\\mars";.

Community
  • 1
  • 1
Blas Soriano
  • 566
  • 3
  • 16
  • this.name stands for the name of the planet. I'm sorry i didnt mention that a planet has its name. so, the name of the planet is a simple name, and, lets say, asteroid1.Name is the path to the sprite. As you didn't notice, every time i load Texture2D, i do it with Cont.Texture2D(string path), and that function returns a new Texture2D with its name as the path it was loaded from. – Monset Jun 21 '15 at 12:18
  • @Monset added explanations by editing my answer. About FPS, you could use [BinaryReader.ReadSingle()](https://msdn.microsoft.com/en-us/library/system.io.binaryreader.readsingle(v=vs.110).aspx) for floats, and [BinaryReader.ReadDouble()](https://msdn.microsoft.com/en-us/library/system.io.binaryreader.readdouble(v=vs.110).aspx) for double. – Blas Soriano Jun 21 '15 at 14:06
  • as you said, "name" is the name of the instance of class Planet, and "planet" is an animation of planet in class Planet, that has a Texure2D, named Sprite. Excuse my poor variable naming system, I didn't pay much attention to it in this class. When I am saving the planet to a file, first I save the name of the planet, and 2nd I save *path* to the planet animation sprite.There are no errors or missunderstandings about that.My problem is of another sort. – Monset Jun 21 '15 at 14:29
  • I'll explain it once more. First I save the newly made planet via test function, then comment that, then try to load the saved file (the file is acctually found, its not that file couldn't be found), but the LoadFromFile function, in line ---this.planet = new Animation(Cont.Texture2D(this.name),...--- goes to Cont's function "public static Texture2D (string path)", and that function can't find the sprite, even though that exact sprite was found when I made the planet before saving it. – Monset Jun 21 '15 at 14:33
  • @Monset OK, I understood now. That sprite.name cannot be null, or you would get a NullArgumentException when using `bw.Write(planet.Sprite.Name);`. So the problem must be related to the string content itself. I would do 2 things: 1- Set a breakpoint in that line to inspect that string. 2- Use a hexadecimal editor to inspect the binary file. Check [this screenshot](http://i.imgur.com/HMjQssl.png). – Blas Soriano Jun 21 '15 at 15:32
  • I set a breakpoint with "throw new NotSupportedException(path)" if Cont.Texture2D couldn't get the image file, and it seems that the path is an empty string – Monset Jun 21 '15 at 16:28
  • Are you using more than one instance of ContentManager? Inside `testFunction()` I see `Cont` in the commented code, and `Content` in the uncommented code. – Blas Soriano Jun 21 '15 at 17:27
  • there is one ContentManager in Game1 class (a default XNA class to start your game in) and that CM is loaded in static Cont class – Monset Jun 22 '15 at 09:29