The question is: if I want a simple file format to describe the objects in my game level, what would it look like?

First, I'd recommend using XML. There are many good parsers out there, including my simple and fast XMLSCAN library.

If you don't want XML, then you can define a file format that just lists the types and names of objects, and the parameters for each object.

Here's the typical parser loop for reading objects:

  size_t size;
  char * text = read_file( "level.txt", &size );
  char * del = text;
  char * end = size+text;

  while( text < end ) {
    get_object( text );
  }
  delete[] del;

The helper function to read a file into memory looks like this:

  char * read_file( char const * fn, size_t * osize )
  {
    FILE * f = fopen( fn, "rb" );
    if( !f ) throw std::runtime_error( "Can't find file" );
    fseek( f, 0 , 2 );
    *osize = ftell( f );
    fseek( f, 0, 0 );
    char * ret = new char[*osize+1];
    fread( ret, 1, *osize, f );
    ret[*osize] = 0;
    fclose( f );
    return ret;
  }

"getting" and "object" from a text file means parsing the file. There are a number of ways to write a parser; the simplest is a recursive-descent parser ("RD").

  // assuming syntax is:
  // obj-type obj-name {
  //   parameter value;
  //   parameter value;
  // }
  void get_object( char * & text )
  {
    std::string type = get_token( text );
    std::string name = get_token( text );
    if( get_token( text ) != '{' ) throw std::runtime_error( "syntax error; '{' expected" );
    std::list< std::pair< std::string, std::string > > params;
    while( true ) {
      std::string param = get_token( text );
      if( tok == '}' ) break; // done
      std::string value = get_token( text );
      if( get_token( text ) != ';' ) throw std::runtime_error( "syntax error; ';' expected" );
      params.push_back( std::pair< std::string, std::string >( param, value ) );
    }
    register_object( type, name, params );
  }

OK, all we need to know now is how to read a token from a character pointer. In the simplest case, we assume that tokens are non-whitespace, non-semicolon and non-brace characters, separated by semicolons, braces, or whitespace. To support strings with spaces in them, let's use double-quote for that, and backslash-doublequote to quote a quote.

  std::string get_token( char const * & text )
  {
    while( *text && isspace( *text ) ) ++text;
    if( !*text ) throw std::runtime_error( "get_token() encountered end of file" );
    if( *text == '{' || *text == '}' || *text == ';' ) { ++text; return std::string(text[-1], 1); }
    bool quote = false;
    bool backslash = false;
    std::string ret;
    while( *text ) {
      if( !quote && !backslash ) {
        if( *text == '{' || *text == '}' || *text == ';' || isspace( *text ) ) break;
        if( *text == '"' ) quote = true;
        else if( *text == '\\' ) backslash = true;
        else ret.push_back( *text );
      }
      else if( backslash ) {
        ret.push_back( *text );
        backslash = false;
      }
      else {
        if( *text == '"' ) quote = false;
        else ret.push_back( *text );
      }
      ++text;
    }
    if( quote || backslash ) throw std::runtime_error( "get_token() found end of file within string value" );
    return ret;
  }

It's really not that complicated. Fancification of this method may include supporting datatypes other than strings for parameters (vectors, quaternions, integers, etc), and maybe even supporting sub-objects. Typically, you'll implement a variant type to use instead of "string" for the values in this case.

So, what do you do with the parsed objects that you get into register_object()? Well, that's where the specifics of your program comes in. If you want to store all the parameters as-is and use them for runtime look-up, you probably want to use a std::multi_map instead of a std::list. Else, you can just parse through the list of parameter values, and stuff the given value into fields of your manufactured objects inside register_object(). Other approaches are possible too; it really depends on what your game is doing!