This topic is 4353 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

Hello everybody, I have a rather simple problem, but somehow I can't manage to get around it. I have a working sidescrolling-shooter game and now I want to add levels. I use a simple text file for storing the level information. At the moment, I only have three pieces of information in this level file: what image to use as background for this level, what image as middleground and what as foreground. This is important because I want each level to be looking different from the last one. I use three layers, because I use parallax scrolling. Anyways, later on, I will also add information about the position and type of enemies etc. to this file, but that's not the point right now. I want to read the filenames for my image files for all three layers. The level file looks exactly like this at the moment:
[images]
background: sprites/background.bmp
middleground: sprites/middleground.bmp
foreground: sprites/foreground.bmp

And this is the code, with which I try to load this information (those three strings) into my program (straight from my level.load-function):
void clevel::load ( SDL_Surface *screen_surface, const char* filename )
// loads a level from a level file
{
FILE	*file;
char	line[255];
string	background_filename;
string	middleground_filename;
string	foreground_filename;
int		enemy_count;

// open the file
file = fopen ( filename, "rt" );

if ( !file )
// if there was an error
return_error ( "Failed to load level..." );

// read first line and ignore it

// read second line and scan background filename
sscanf ( line, "background: %s\n", &background_filename );

// read third line and scan middleground filename
sscanf ( line, "middleground: %s\n", &middleground_filename );

// read fourth line and scan foreground filename
sscanf ( line, "foreground: %s\n", &foreground_filename );

// close the file again
fclose ( file );

// now initialize the images
background.init ( screen_surface, background_filename.c_str ( ), 0.3f );
middleground.init ( screen_surface, middleground_filename.c_str ( ), 0.7f );
foreground.init ( screen_surface, foreground_filename.c_str ( ), 1.2f );
}


My problem is that the game now crashes, because I didn't read proper values out as filenames. The init-function for the back-, middle- and foreground in the last three lines of the presented code expect const char*-values - but I don't know how to convert the strings into that format properly. I also tried to read out the filenames as const char*-values rightaway using %c instead of %s, but that only seems to work for single tokens and not a series of characters (a string). Any help? I know there has to be some way, but I am just not confident enough with those things yet.

##### Share on other sites
Hmm... Okay, could you however give me an example of how to ditch "FILE" for example? I am not yet so experienced with that std::string class.

##### Share on other sites
Quote:
 Original post by d h kHmm... Okay, could you however give me an example of how to ditch "FILE" for example? I am not yet so experienced with that std::string class.EDIT: Hey, where has your reply gone, jyk?
I made a mistake in my first post and deleted it; sorry about that. Anyway, the advice to switch to the corresponding C++ classes and functions holds. I'd write out an example, but I don't use this sort of file streaming that much and I'm not sure of the syntax off the top of my head. I'm sure someone else could whip up and example in no time though...

##### Share on other sites
You cannot read std::string like that. A std::string is a class and as such it is a record of data that, among other things, contains a (heap!) pointer to character data. If you read dta from a file into a string object like this (i.e. using sscanf), you will not only overwrite the additional data that the string object stores, but also cause an invalid memory access. This is because a std::string implementation will most likely allocate memory to store the character data.
Simply writing data to it is similar to having a plain char* and reading into it without allocating memory first.

Without altering your FILE-based code, a valid solution would look something like this:
void clevel::load ( SDL_Surface *screen_surface, const char* filename )// loads a level from a level file{	FILE	*file;	char	line[255];	char	filename[256];	int		enemy_count;        // add some more error-handling        bool readOk = true;		// open the file	file = fopen ( filename, "rt" );	if ( !file )	// if there was an error 		return_error ( "Failed to load level..." );	// read first line and ignore it	read_string ( file, line );	// read second line and scan background filename	read_string ( file, line );        filename[0] = '\0';	readOk = sscanf ( line, "background: %s\n", filename ) == 1;        if ( readOk )   	    background.init ( screen_surface, filename, 0.3f );        else {            fclose( file );            return_error ( "Failed to read background image..." );                    }	// read third line and scan middleground filename	read_string ( file, line );        filename[0] = '\0';	readOk = sscanf ( line, "middleground: %s\n", filename ) == 1;        if ( readOk )  	    middleground.init ( screen_surface, filename, 0.7f );        else {            fclose( file );            return_error ( "Failed to read middleground image..." );                    }	// read fourth line and scan foreground filename	read_string ( file, line );        filename[0] = '\0';	readOk = sscanf ( line, "foreground: %s\n", filename ) == 1;        if ( readOk )  	    foreground.init ( screen_surface, filename, 1.2f );        else {            fclose( file );            return_error ( "Failed to read foreground image..." );                    }	// close the file again	fclose ( file );}

You can also use the file streams from the C++ stdlib instead of FILE. This would allow you to use std::string objects instead of fixed size and hence error-prone character arrays (i.e. buffer overflow issues).

HTH,
Pat.

##### Share on other sites
Here you go:

#include <fstream>/* ... */std::ifstream file("level.txt");if (!file) {    /* error handling goes here */std::string skip;std::string	background_filename;std::string	middleground_filename;std::string	foreground_filename;    file >> skip; // [images]file >> skip; // background:file >> background_filename;file >> skip; // middleground:file >> middleground_filename;file >> skip; // foreground:file >> foreground_filename;background.init ( screen_surface, background_filename.c_str ( ), 0.3f );middleground.init ( screen_surface, middleground_filename.c_str ( ), 0.7f );foreground.init ( screen_surface, foreground_filename.c_str ( ), 1.2f );

This is a somewhat crude example, but it will get the job done. More robust code would parse the file in a more generic manner and check for and report any errors encountered. The above should get you started though.

[Edit: darookie's example is a better example of proper error-checking and so forth, but, as he suggests, you'll be better off using the C++ standard library as in the above example. So just combine the two examples and you should be all set :-)]

##### Share on other sites
Ah, very nice. Thanks to you both for the quick help. R++!! :)

##### Share on other sites
Oh, but now I need to read in some integer values, is therr an easy way to do this with those new functions, or will I need to use "my" old way for numbers?

EDIT: Nevermind! Figured it out on my own... Easy stuff! :)

[Edited by - d h k on June 26, 2006 9:50:33 AM]

• 10
• 17
• 9
• 14
• 41