If you can blow chunks out of it in the game ...
Well then, don't hint at "blowing chunks" out of anything, whilst supporting the idea of RLE.
PoV: The method that you used increases the file size to 3.0MB, double what it was before. I think I'm going to have to take it to the next level and encode it as binary data like you said, but I have no idea how to do it.
It might sound strange, but that's actually good. Understanding why is important to the "next step", the idea of binary encoding.
So, in your original example, you were writing text to the file. An ASCII '0' or '1', followed by an ASCII newline character (0x13 and/or 0x10, for simplicity, lets assume it's only 1 byte for the newline). That's two bytes per pixel. To contrast, what we've written with the binary file example is the entire 4 bytes of of an integer, which is as you noted, twice the size of the text representation.
So, the solution to this problem is to somehow write less data per pixel to the file. Given that you're only need 1 bit of information per pixel, that's our ideal size. However, a first step could be to go from writing 2-4 bytes, and reduce that to writing 1 byte per pixel.
Bytes can hold values between 0-255 (or -128 and 127), and a byte is 8 bit's long. Bytes are our basic measurement in computers, and are out minimum unit of space we can use. What that means, is even though we only want 1 bit of information, we're required to use up at least 1 whole byte to do it. Bytes represent basic text characters (A, B, C, 1, 2, 3, ...). "C style strings" are arrays of bytes, followed by a terminator character ('H', 'e', 'l', 'l', 'o', 0). Integers, pointers, and float's are built up of 4 bytes.
In C/C++, our unit of bytes is represented by the types 'char' and 'unsigned char'. So the easy/lame solution is to move your data to a 'char', then write it.
ofstream MapFile("MyMap.map", ios::out | ios::binary);
int MyVariable = 25;
char MyByte = (char)MyVariable;
MapFile.write( &MyByte, sizeof(char) );
ifstream MapFile("MyMap.map", ios::in | ios::binary);
MapFile.read( &MyByte, sizeof(char) );
MyVariable = (int)MyByte;
However, all this does for us is reduce the data to 1 byte in size, half of your smallest file so far.
Now, how to encode in to binary.
As it's been mentioned, binary is that whole 0 and 1 thing. As far as whole numbers are concerned, binary is also known as the "base 2" number system. Decimal is "base 10". That means for every digit (i.e. character), there are that many options. Every digit in a decimal number is between 0-9, and every digit in binary is between 0-1.http://en.wikipedia.org/wiki/Binary_numeral_system
I'm going to assume you know what binary is for the rest of this, as well as it's basic operations (addition, subtraction, shifting, "OR", "AND", "XOR"). Review the above link to learn more/brush up on it.
Now, as it was said earlier, our smallest unit we can deal with on computers is the byte, not the bit. To use bits, we need to cram/fit them in to a byte. Since a byte is 8 bits in size, we can fit 8 of them in one.
So, what we want to do is change our reading and writing code to work with 8 pixels at one time.
ofstream LFile("LevelFile.lvl", ios::out | ios::binary);
for (int SCheckX = 0; SCheckX < 1024; SCheckX+=8)
for (int SCheckY = 0; SCheckY < 768; SCheckY++)
Inside that, we'll be doing something with 8 of our pixels. Note that I changed the X to increment by 8, instead of 1.
To work with the idea of binary, we need to be sure our numbers are 0 and 1. The number 1 in an integer or a byte is actually the equivalent of setting the first bit on. 2 in an integer or byte is the 2nd bit on. 4 in an integer or byte is the 3rd bit on, and so on.
We can move bit's around an integer or byte using shifts. Down shift ">>" and up shift "<<". An easy way to remember which is which is to think of the symbol as an arrow, and to look at a number. "500". If I wan to add another zero (5000), that'd be bigger. If I was to remove a zero (50), that'd be smaller. Up and down. Up shifting makes that number bigger (doubles it to be exact). So moving the number that way (<-), and it gets bigger (hence, "up"). "500 << 1" becomes "1000" after up shifting. Down shifting half's the number, rounding down. "500 >> 1" becomes "250". Multiple shifts is like applying a shift multiple times. "500 >> 2" would be 125, a quarter the size of the original, or 4x the size if shifting up. Doing it again would be an 8th the size shifting down, or 8x the size if shifting up.
This is actually a binary operation behind the scenes. Your number 50 in binary is "110010". Shifting up, the binary number becomes "1100100", aka 100 in decimal. Shifting down, the binary number becomes "11001", aka 25. Shift that down again, and that right side 1 gets clipped off ("1100"), resulting in 12. That's also an easy way to know if a number is odd or even (checking that first bit).
So, to encode our 8 pixels in to a byte, we need to fit each one in to a binary slot of a byte. The first one is fine where it is (MyNumbers[X]). The 2nd one needs to be shifted up by 1 (MyNumbers[X+1] << 1). The 3rd one needs to be shifted up 2 slots (MyNumbers[X+2] << 2). And so on, up to the last one (MyNumbers[X+7] << 7). That'll give us individual binary bits. Now to merge them in to one byte.
There's actually 3 ways to do this. Adding the numbers together (+), XOR'ing the numbers together (^), or OR'ing the numbers together (|). Technically in this case, they'll all do the same thing, but if there was bit overlap, the results of each operation would be different. Our ideal operation for merging bit's is OR'ing.
int Bits = (Num[X+0] << 0);
Bits |= (Num[X+1] << 1);
Bits |= (Num[X+2] << 2);
Bits |= (Num[X+3] << 3);
Bits |= (Num[X+4] << 4);
Bits |= (Num[X+5] << 5);
Bits |= (Num[X+6] << 6);
Bits |= (Num[X+7] << 7);
I used the "OR Equals" operator because this wouldn't have fit cleanly on 1 line.
The above is what we'd do to combine all the bits for each pixel together in to a single number. Convert that to a "char", and write it as binary to a file.
This post is long, so I'll do reading in a new one.