How to output to a file?

Monkey Forums/Monkey Programming/How to output to a file?

Aman(Posted 2011) [#1]
Is there a way to output data to a file?

I tried SaveString function but it didn't work. I noticed that it is on a different module called os but it looks like it doesn't support most targets as it only has cpp in the native folder.

What is the difference between LoadString function that's in the mojo module and the one in the os module?

Is there a module that add support to this functionality?

I think this is a very important and handy feature and would be surprised if monkey still lacks it at this point.


Beaker(Posted 2011) [#2]
The native LoadString doesn't actually get the string from a file, the string is actually embedded in the source code.

What target do you want to work on? It isn't possible for Html5 or Flash to create a file.

Also, have you looked at the SaveState() command?


Goodlookinguy(Posted 2011) [#3]
Monkey is meant to be a multiplatform programming language. Since you aren't going to be able to save files in Flash and HTML, you shouldn't expect this functionality.

The OS module was likely intended for people who wanted to make tools, not for the game's themselves.

os.LoadString = Loads a file from anywhere in the system, and the binary executable is the starting path
mojo.LoadString = Loads a file from the data folder if on certain platforms, otherwise the files are embedded and loaded internally.

SaveState = Will save to the only available file that can be altered
LoadState = Will load from the only available file that can be altered

The closest thing to multiple file manipulation that you'll get is one of the virtual file systems. Although, that's probably not what you're looking for.

Goodlookinguy: Simple File System
GfK: File System


benmc(Posted 2011) [#4]
I think writing it off because of multiple targets is unfortunate.

Couldn't Flash and HTML5 save to cookies as if they were files to get around this? Then have file-system access on iOS/Android/GLFW. I'm not familiar with XNA for win phone or xbox, but I assume there is some alternative if not file access as well.

File system access is possible on mobile apps, so it should be in Monkey if even just for mobile targets. You can easily specify valid targets in modules and your code.


Dabz(Posted 2011) [#5]
Erm, something I knocked up for my own use when testing, bit basic but it does me as I want to save stuff other then in the data folder:-

{approot}/nativefilesystem/nativefilesystem.monkey

#if TARGET="glfw"
Import "native/nativefilesystem.${TARGET}.${LANG}"
#EndIf

Extern

#If TARGET="glfw" Then
	Function WriteFile:int(path:string) = "NativeFileSystem::WriteFile"
	Function WriteLine:void(line:string) = "NativeFileSystem::WriteLine"
	Function ReadFile:int(path:string) = "NativeFileSystem::ReadFile"
	Function ReadLine:string() = "NativeFileSystem::ReadLine"
	Function CloseFile:void() = "NativeFileSystem::CloseFile"
#Else
	Public
	Function WriteFile:int(path:string)
		Return 0
	end
	
	Function WriteLine:void(line:string)
	End
	
	Function ReadFile:int(path:string)
		Return 0
	End
	
	Function ReadLine:string()
		Return ""
	End
	
	Function CloseFile:void()
	end
#EndIf 


{approot}/native/nativefilesystem.glfw.cpp

#include <stdio.h>

typedef wchar_t OS_CHAR;

static char *C_STR( const String &t ){
	return t.ToCString<char>();
}

static OS_CHAR *OS_STR( const String &t ){
	return t.ToCString<OS_CHAR>();
}

FILE *pFile;

class NativeFileSystem
{
	public:
		static int WriteFile(String path)
		{
		pFile = _wfopen(OS_STR(path),L"w");
		if(pFile!=NULL)
		{
			return 1;
		}
		return 0;
	}
	
	static int ReadFile(String path)
	{
		pFile = _wfopen(OS_STR(path),L"r");
		if(pFile!=NULL)
		{
			return 1;
		}
		return 0;
	}
	
	static String ReadLine()
	{
		wchar_t newString[256];
		fgetws(newString,256,pFile);
		
		String line = String(newString);
		return line;
	}
	
	static void WriteLine(String line)
	{
		String newLine = line+"\n";
		fputws(OS_STR(newLine),pFile);
		
	}
	
	
	static void CloseFile()
	{
		fclose(pFile);
	}
	
};


Example:-

Method SaveStats:Void()
		Local timeFinished:Int = levelTimer
		Local bestTime:Int
		
		Local statString:String
		
		
		
		local result:Int = ReadFile("data\store\"+(currentLevel)+"stat.txt")

		bestTime = int(ReadLine()) 
		CloseFile() 
		
		if timeFinished > bestTime
		
			statString = timeFinished
			Local pathString:String
			if IDE_TEST = True
				pathString = "..\..\..\..\Match3.data\store\"+(currentLevel)+"stat.txt"
			Else
				pathString = "data\store\"+(currentLevel)+"stat.txt"
			EndIf
			
			WriteFile(pathString)
			WriteLine(statString)
			CloseFile()
		EndIf 
	End


Obviously no use what's so ever other then GLFW (In Windows), but, if your looking at file system stuff on other targets, your in for some pain!

Dabz


Aman(Posted 2011) [#6]
I use SaveState and LoadState all the time for my settings and believe me, I have lots of them. I needed something so that I can write log file.

benmc, SaveState and LoadState in html5 and flash are actually using cookies. reading and writing to files doesn't make sense that much in those targets. But that doesn't mean it is not necessary for other targets.

Thanks Dabz. I just managed to do something similar but for android.

Here is my code(Modified so it can be easily added to diddy):

in diddy/native/diddy.android.java:
import android.content.Context;   // necessary but already there in diddy.android.java

// add this line to the top of the file:
import java.io.FileOutputStream;

class diddy
{
	//add the following 2 methods to the top of the diddy class:
	static void saveStringToFile(String message, String filename)
	{
		FileOutputStream fos;
		try {
			fos = MonkeyGame.view.getContext().openFileOutput(filename, Context.MODE_PRIVATE);
			fos.write(message.getBytes());
			fos.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}catch(IOException e){
			e.printStackTrace();
	}
	}
	
	static String loadStringFromFile(String filename)
	{
		FileInputStream fis;
		try {
			fis = MonkeyGame.view.getContext().openFileInput(filename);
			fis.read();
			ByteArrayOutputStream content = new ByteArrayOutputStream();
			int readBytes = 0;
			byte[] sBuffer = new byte[512];
			while ((readBytes = fis.read(sBuffer)) != -1) {
				content.write(sBuffer, 0, readBytes);
			}
			String message = new String(content.toByteArray());
			fis.close();
			return message;
		} catch (FileNotFoundException e) {
            e.printStackTrace();
        }catch(IOException e){
            e.printStackTrace();
        }
		return "";
	}
}

in diddy/functions.monkey:
#If LANG="java" Then
	Function SaveStringToFile:Void(message:String, filename:String) = "myGUI.saveStringToFile"
	Function LoadStringFromFile:String(filename:String) = "myGUI.loadStringFromFile"
#Else
	Function SaveStringToFile:Void(message:String, filename:String)
	End
	Function LoadStringFromFile:String(filename:String)
		return "Target Not Supported"
	End
#End

This will Open a private file associated with this Context's application package for writing "SaveStringToFile" or reading "LoadStringFromFile". SaveStringToFile Creates the file if it doesn't already exist. File will not be viewable or accessible unless you change MODE_PRIVATE to:
MODE_APPEND to append to an existing file
MODE_WORLD_READABLE to control reading permissions.
MODE_WORLD_WRITEABLE to control writing permissions.

and here is how to use it:
str:String =LoadStringFromFile("log.txt")
SaveStringToFile(str,"log.txt")


Now we need someone to write the code for XNA and ask therevills to add it to diddy :P


Aman(Posted 2011) [#7]
and That is the native code for XNA:

public static void saveStringToFile(string message, string filename)
{
#if WINDOWS
	FileStream stream = File.Open(filename, FileMode.OpenOrCreate);
#else
	IsolatedStorageFileStream stream = new IsolatedStorageFileStream(
		filename, FileMode.OpenOrCreate, IsolatedStorageFile.GetUserStoreForApplication());
#endif
	using(StreamWriter writer = new StreamWriter(stream)) 
	{ 
		writer.Write(message); 
	}
}

public static string loadStringFromFile(string filename)
{
	string message;
#if WINDOWS
	FileStream stream = File.Open(filename, FileMode.OpenOrCreate);
#else
	IsolatedStorageFileStream stream = new IsolatedStorageFileStream(
		filename, FileMode.OpenOrCreate, IsolatedStorageFile.GetUserStoreForApplication());
#endif
	using (StreamReader reader = new StreamReader(stream)) 
	{ 
		message = reader.ReadToEnd().ToString(); 
	}
	return message;
}


This will open or create a file in the same directory the app is running from. this has to be in a .cs file inside a class(just type "class something{" at the top and close it at the end. Can be used just like the android one on top of this post.

#Edit:
Had to change the code a little bit so that it works with windows as well as windows phone and xbox360