File I/O using COCOS2D-X
Asked Answered
B

3

6

I'm trying to load up a Comma Separated file called POSDATA.GAMEDATA. I've looked up several places on the internet and it turns out I need to do some tweaking and / or a different class.

I tried using ifstream. However, it cannot open the file. Xcode 4.3.2 cannot seem to find my POSDATA.GAMEDATA file. I also tried to make the file using ofstream but when I use open() in both cases, the file is not opened.

My code is something like this:

using namespace std;
void FileLoader::loadFile( string p_WhichFile ) {
   // Local Variables
   string thisLine;

   // Open POSDATA.GAMEDATA
   ifstream dataStream;
   dataStream.open( p_WhichFile.c_str( ) );

   // Check if file is opened
   if ( !dataStream ) {
      cerr << "[ ERROR ] Cannot load file:" << p_WhichFile.c_str( ) << endl;
      exit( 1 );
   }

   // Get lines of strings
   while ( getline( dataStream, thisLine ) ) {
      fileContents.push_back( thisLine ); // fileContents is a vector< string > object
   }

   dataStream.close( );
   cout << "[ NOTICE ] Finished reading file" << p_WhichFile << endl;
}

I've seen CCFileUtils but I can't seem to get how to use it.

EDIT: I've tried supplying the absolute path ( /Users/LanceGray/Documents/LanceDev/COCOS2DX/cocos2dx/TestGame/Data/POSDATA.GAMEDATA ) and it worked. However, I cannot do this since the game is supposed to be used in iOS devices and Android, so the path is not always the same on each device. Any help will be grealy appreciated.

Bulletin answered 29/8, 2012 at 5:31 Comment(2)
you are doing it on Android or iOS? if android or you are planning to support both I guess you'd better use CCFileUtils to avoid getting in to the zip lib. On the other hand, if iOS, you can just use fopen. I am not very familiar with ifstream, cannot help much.Factfinding
It's supposed to be for both iOS and Android. Can you give a small example on how to use CCFileUtils and fopen? I'll try to look for them in the meantime.Bulletin
B
17

I got working by using CCFileUtils( )::sharedFileUtils( ) -> fullPathFromRelativePath( "POSDATA.GAMEDATA" );

A more detailed explanation:

  1. Add the files that you need in the Project by going to the Project Tree on the left and Right-click -> Add Files. What I did was I added a new folder called Data on the same level as the Resources and Classes folders and placed my POSDATA.GAMEDATA file there. In Xcode, I added a new group and added that file in that group.
  2. Then I used ifstream to open the file.
  3. When opening the file, use CCFileUtils( )::sharedFileUtils( ) -> fullPathFromRelativePath( ) to get the absolute path of the file. Supply the file name on the fullPathFromRelativePath( ) as argument.
  4. Try to run it and it should work fine.

A small example:

// FileReader.h
#include "cocos2d.h"

using namespace std;
using namespace cocos2d;

class FileReader {
private:
   vector< string > mFileContents;

public:
   FileReader( string pFileName, char pMode = 'r' );
};

// FileReader.cpp
#include "FileReader.h"
#include <fstream>
#include "cocos2d.h"

using namespace cocos2d;
using namespace std;

FileReader::FileReader( string pFileName, char pMode ) {
   // Create input file stream
   ifstream inputStream;
   string thisLine;

   // Open file
   inputStream.open( CCFileUtils( )::sharedFileUtils( ) -> fullPathFromRelativePath( pFileName ).c_str( ) );

   // Check if it is open
   if ( !inputStream.is_open( ) ) {
      cerr << "[ ERROR ] Cannot open file: " << pFileName.c_str( ) << endl;
      exit( 1 );
   }

   while ( getline( inputStream, thisLine ) ) {
      // Put all lines in vector
      mFileContents.push_back( thisLine );
   }

   inputStream.close( );
   cout << "[ NOTICE ] Finished opening file: " << pFileName.c_str( ) << endl;
}

This class will load a file with the name pFileName and place it on its member variable mFileContents. ( Note that it should have a public get function like vector< string > getFileContents( ) to access the mFileContents because it is private )

EDIT: The above sample will work on iOS, however, it won't on Android devices. So to fix this, instead of using ifstream, use CCFileUtils::sharedUtils( ) -> getFileData( ) instead. In conjunction with CCFileUtils::sharedUtils( ) -> fullPathFromRelativePath( ), we will be able to achieve our goal of reading a plain text file that works on both iOS and Android.

The FileReader class would then be like this:

// FileReader.cpp
#include "FileReader.h"
#include <fstream>
#include "cocos2d.h"

using namespace cocos2d;
using namespace std;

FileReader::FileReader( string pFileName, char pMode ) {
   // Initialize variables needed
   unsigned long fileSize = 0;
   unsigned char * fileContents = NULL;
   string thisLine, result, fullPath, contents;

   // Get absolute path of file
   fullPath = CCFileUtils::sharedFileUtils( ) -> fullPathFromRelativePath( pFileName.c_str( ) );

   // Get data of file
   fileContents = CCFileUtils::sharedFileUtils( ) -> getFileData( fullPath.c_str( ) , "r", &fileSize );
   contents.append( ( char * ) fileContents );

   // Create a string stream so that we can use getline( ) on it
   istringstream fileStringStream( contents );

   // Get file contents line by line
   while ( getline( fileStringStream, thisLine ) ) {
      // Put all lines in vector
      mFileContents.push_back( thisLine );
   }

   // After this, mFileContents will have an extra entry and will have the value '\x04'.
   // We should remove this by popping it out the vector.
   mFileContents.pop_back( );

   // Delete buffer created by fileContents. This part is required.
   if ( fileContents ) {
      delete[ ] fileContents;
      fileContents = NULL;
   }

   // For testing purposes
   cout << "[ NOTICE ] Finished opening file: " << pFileName.c_str( ) << endl;
}
Bulletin answered 29/8, 2012 at 6:33 Comment(1)
What if you have a huge data file on android and can't afford to read the entire file with getFileData()? It would be nice to have more direct access... like fseekMachado
F
2
// For versions less than v2.0.1
// The version I am using is 0.12.0
unsigned long fileSize = 0;
char* pBuffer = CCFileUltils::getFileData("relative_path","r",&fileSize);
CCLOG("Data is %s",pBuffer);
Factfinding answered 29/8, 2012 at 5:45 Comment(3)
Thanks for the help. I've got it working by using CCFileUtils, though.Bulletin
I tried using getFileData() but I get an error saying Assertion failed: (pszFileName != __null && pSize != __null && pszMode != __null), function getFileData, file CCFileUtils.mm, line 450. I used it like this: unsigned char * posdataLoc = CCFileUtils::sharedFileUtils() -> getFileData( "POSDATA.GAMEDATA", "r", 0 );Bulletin
I got getFileData( ) working now. When I cout it on the console, it shows extra random characters on the end. Weird.Bulletin
P
0

You can reference from Cocos's wiki Read/write file in cocos2d

Partnership answered 1/2, 2015 at 4:44 Comment(1)
Don't copy and paste the same link in multiple questions. This is not an answer but a comment at best.Gyrostat

© 2022 - 2024 — McMap. All rights reserved.