diff options
author | tysand@chromium.org <tysand@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-06-11 22:00:21 +0000 |
---|---|---|
committer | tysand@chromium.org <tysand@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-06-11 22:00:21 +0000 |
commit | 50512e3ea26652015d57078133e6df150299a2ab (patch) | |
tree | 2f7eed4d47fcebd3722be8694aa24276a35bb55e /native_client_sdk/src | |
parent | b1af5ccae55c21956ba9e93a0aba21972caf3fa4 (diff) | |
download | chromium_src-50512e3ea26652015d57078133e6df150299a2ab.zip chromium_src-50512e3ea26652015d57078133e6df150299a2ab.tar.gz chromium_src-50512e3ea26652015d57078133e6df150299a2ab.tar.bz2 |
New SDK example making use of the persistent file storage
Adding file_io SDK example
R=noelallen@chromium.org
BUG=132123
TEST=
Review URL: https://chromiumcodereview.appspot.com/10513018
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@141519 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'native_client_sdk/src')
-rwxr-xr-x | native_client_sdk/src/build_tools/build_sdk.py | 1 | ||||
-rw-r--r-- | native_client_sdk/src/examples/file_io/Makefile | 76 | ||||
-rw-r--r-- | native_client_sdk/src/examples/file_io/file_io.cc | 444 | ||||
-rw-r--r-- | native_client_sdk/src/examples/file_io/file_io.html | 171 | ||||
-rw-r--r-- | native_client_sdk/src/examples/file_io/file_io.nmf | 6 | ||||
-rw-r--r-- | native_client_sdk/src/examples/index.html | 9 |
6 files changed, 706 insertions, 1 deletions
diff --git a/native_client_sdk/src/build_tools/build_sdk.py b/native_client_sdk/src/build_tools/build_sdk.py index dbcfcc2..fe55b92 100755 --- a/native_client_sdk/src/build_tools/build_sdk.py +++ b/native_client_sdk/src/build_tools/build_sdk.py @@ -352,6 +352,7 @@ EXAMPLE_MAP = { 'newlib': [ 'debugging', 'file_histogram', + 'file_io', 'fullscreen_tumbler', 'gamepad', 'geturl', diff --git a/native_client_sdk/src/examples/file_io/Makefile b/native_client_sdk/src/examples/file_io/Makefile new file mode 100644 index 0000000..4c665c7 --- /dev/null +++ b/native_client_sdk/src/examples/file_io/Makefile @@ -0,0 +1,76 @@ +# Copyright (c) 2012 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# +# GNU Make based build file. For details on GNU Make see: +# http://www.gnu.org/software/make/manual/make.html +# + +# +# Project information +# +# These variables store project specific settings for the project name +# build flags, files to copy or install. In the examples it is typically +# only the list of sources and project name that will actually change and +# the rest of the makefile is boilerplate for defining build rules. +# +PROJECT:=file_io +LDFLAGS:=-lppapi_cpp -lppapi +CXX_SOURCES:=$(PROJECT).cc + + +# +# Get pepper directory for toolchain and includes. +# +# If PEPPER_ROOT is not set, then assume it can be found a two directories up, +# from the default example directory location. +# +THIS_MAKEFILE:=$(abspath $(lastword $(MAKEFILE_LIST))) +NACL_SDK_ROOT?=$(abspath $(dir $(THIS_MAKEFILE))../..) + +# Project Build flags +WARNINGS:=-Wno-long-long -Wall -Wswitch-enum -pedantic -Werror +CXXFLAGS:=-pthread -std=gnu++98 $(WARNINGS) + +# +# Compute tool paths +# +# +OSNAME:=$(shell python $(NACL_SDK_ROOT)/tools/getos.py) +TC_PATH:=$(abspath $(NACL_SDK_ROOT)/toolchain/$(OSNAME)_x86_newlib) +CXX:=$(TC_PATH)/bin/i686-nacl-g++ + +# +# Disable DOS PATH warning when using Cygwin based tools Windows +# +CYGWIN ?= nodosfilewarning +export CYGWIN + + +# Declare the ALL target first, to make the 'all' target the default build +all: $(PROJECT)_x86_32.nexe $(PROJECT)_x86_64.nexe + +# Define 32 bit compile and link rules for C++ sources +x86_32_OBJS:=$(patsubst %.cc,%_32.o,$(CXX_SOURCES)) +$(x86_32_OBJS) : %_32.o : %.cc $(THIS_MAKE) + $(CXX) -o $@ -c $< -m32 -O0 -g $(CXXFLAGS) + +$(PROJECT)_x86_32.nexe : $(x86_32_OBJS) + $(CXX) -o $@ $^ -m32 -O0 -g $(CXXFLAGS) $(LDFLAGS) + +# Define 64 bit compile and link rules for C++ sources +x86_64_OBJS:=$(patsubst %.cc,%_64.o,$(CXX_SOURCES)) +$(x86_64_OBJS) : %_64.o : %.cc $(THIS_MAKE) + $(CXX) -o $@ -c $< -m64 -O0 -g $(CXXFLAGS) + +$(PROJECT)_x86_64.nexe : $(x86_64_OBJS) + $(CXX) -o $@ $^ -m64 -O0 -g $(CXXFLAGS) $(LDFLAGS) + + +# Define a phony rule so it always runs, to build nexe and start up server. +.PHONY: RUN +RUN: all + python ../httpd.py + + diff --git a/native_client_sdk/src/examples/file_io/file_io.cc b/native_client_sdk/src/examples/file_io/file_io.cc new file mode 100644 index 0000000..4df8209 --- /dev/null +++ b/native_client_sdk/src/examples/file_io/file_io.cc @@ -0,0 +1,444 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/// @file file_io.cc +/// This example demonstrates the use of persistent file I/O + +#include <stdio.h> + +#include <sstream> +#include <string> + +#include "ppapi/c/ppb_file_io.h" +#include "ppapi/cpp/file_io.h" +#include "ppapi/cpp/file_ref.h" +#include "ppapi/cpp/file_system.h" +#include "ppapi/cpp/instance.h" +#include "ppapi/cpp/module.h" +#include "ppapi/cpp/var.h" +#include "ppapi/utility/completion_callback_factory.h" + +namespace { +/// Used for our simple protocol to communicate with Javascript +const char* const kLoadPrefix = "ld"; +const char* const kSavePrefix = "sv"; +const char* const kDeletePrefix = "de"; +} + +/// The Instance class. One of these exists for each instance of your NaCl +/// module on the web page. The browser will ask the Module object to create +/// a new Instance for each occurence of the <embed> tag that has these +/// attributes: +/// type="application/x-nacl" +/// src="file_io.nmf" +class FileIoInstance : public pp::Instance { + public: + /// The constructor creates the plugin-side instance. + /// @param[in] instance the handle to the browser-side plugin instance. + explicit FileIoInstance(PP_Instance instance) + : pp::Instance(instance), + callback_factory_(this), + file_system_(this, PP_FILESYSTEMTYPE_LOCALPERSISTENT), + file_system_ready_(false) { + } + + virtual ~FileIoInstance() { + } + + virtual bool Init(uint32_t /*argc*/, const char* /*argn*/[], + const char* /*argv*/[]) { + pp::CompletionCallback callback = callback_factory_.NewCallback( + &FileIoInstance::FileSystemOpenCallback); + int32_t rv = file_system_.Open(1024*1024, callback); + if (rv != PP_OK_COMPLETIONPENDING) { + callback.Run(rv); + return false; + } + return true; + } + + protected: + pp::CompletionCallbackFactory<FileIoInstance> callback_factory_; + pp::FileSystem file_system_; + bool file_system_ready_; + + private: + /// Struct to hold various info about a file operation. Our scheme in this + /// example is to allocate this information on the heap so that is persists + /// after the asynchronous call until the callback is called, and is + /// therefore available to the main thread to complete the requested operation + struct Request { + pp::FileRef ref; + pp::FileIO file; + std::string file_contents; + int64_t offset; + PP_FileInfo info; + }; + + /// Handler for messages coming in from the browser via postMessage(). The + /// @a var_message can contain anything: a JSON string; a string that encodes + /// method names and arguments; etc. + /// + /// Here we use messages to communicate with the user interface + /// + /// @param[in] var_message The message posted by the browser. + virtual void HandleMessage(const pp::Var& var_message) { + if (!var_message.is_string()) + return; + + // Parse message into: instruction file_name_length file_name [file_text] + std::string message = var_message.AsString(); + std::string instruction; + std::string file_name; + std::stringstream reader(message); + int file_name_length; + + reader >> instruction >> file_name_length; + file_name.resize(file_name_length); + reader.ignore(1); // Eat the delimiter + reader.read(&file_name[0], file_name_length); + + if (file_name.length() == 0 || file_name[0] != '/') { + ShowStatusMessage("File name must begin with /"); + return; + } + + // Dispatch the instruction + if (instruction.compare(kLoadPrefix) == 0) { + Load(file_name); + return; + } + + if (instruction.compare(kSavePrefix) == 0) { + // Read the rest of the message as the file text + reader.ignore(1); // Eat the delimiter + std::string file_text = message.substr(reader.tellg()); + Save(file_name, file_text); + return; + } + + if (instruction.compare(kDeletePrefix) == 0) { + Delete(file_name); + return; + } + } + + bool Save(const std::string& file_name, const std::string& file_contents) { + if (!file_system_ready_) { + ShowErrorMessage("File system is not open", PP_ERROR_FAILED); + return false; + } + + FileIoInstance::Request* request = new FileIoInstance::Request; + request->ref = pp::FileRef(file_system_, file_name.c_str()); + request->file = pp::FileIO(this); + request->file_contents = file_contents; + + pp::CompletionCallback callback = callback_factory_.NewCallback( + &FileIoInstance::SaveOpenCallback, request); + int32_t rv = request->file.Open(request->ref, + PP_FILEOPENFLAG_WRITE|PP_FILEOPENFLAG_CREATE|PP_FILEOPENFLAG_TRUNCATE, + callback); + + // Handle cleanup in the callback if error + if (rv != PP_OK_COMPLETIONPENDING) { + callback.Run(rv); + return false; + } + return true; + } + + void SaveOpenCallback(int32_t result, FileIoInstance::Request* request) { + if (result != PP_OK) { + ShowErrorMessage("File open for write failed", result); + delete request; + return; + } + + // It is an error to write 0 bytes to the file, however, + // upon opening we have truncated the file to length 0 + if (request->file_contents.length() == 0) { + pp::CompletionCallback callback = callback_factory_.NewCallback( + &FileIoInstance::SaveFlushCallback, request); + int32_t rv = request->file.Flush(callback); + + // Handle cleanup in the callback if error + if (rv != PP_OK_COMPLETIONPENDING) { + callback.Run(rv); + return; + } + } else if (request->file_contents.length() <= INT32_MAX) { + request->offset = 0; + pp::CompletionCallback callback = callback_factory_.NewCallback( + &FileIoInstance::SaveWriteCallback, request); + + int32_t rv = request->file.Write(request->offset, + request->file_contents.c_str(), + request->file_contents.length(), callback); + + // Handle cleanup in the callback if error + if (rv != PP_OK_COMPLETIONPENDING) { + callback.Run(rv); + return; + } + } else { + ShowErrorMessage("File too big", PP_ERROR_FILETOOBIG); + delete request; + return; + } + } + + void SaveWriteCallback(int32_t bytes_written, + FileIoInstance::Request* request) { + // bytes_written is the error code if < 0 + if (bytes_written < 0) { + ShowErrorMessage("File write failed", bytes_written); + delete request; + return; + } + + // Ensure the content length is something write() can handle + assert(request->file_contents.length() <= INT32_MAX); + + request->offset += bytes_written; + + if (request->offset == request->file_contents.length() || + bytes_written == 0) { + // All bytes have been written, flush the write buffer to complete + pp::CompletionCallback callback = callback_factory_.NewCallback( + &FileIoInstance::SaveFlushCallback, request); + int32_t rv = request->file.Flush(callback); + + // Handle cleanup in the callback if error + if (rv != PP_OK_COMPLETIONPENDING) { + callback.Run(rv); + return; + } + } else { + // If all the bytes haven't been written call write again with remainder + pp::CompletionCallback callback = callback_factory_.NewCallback( + &FileIoInstance::SaveWriteCallback, request); + int32_t rv = request->file.Write(request->offset, + request->file_contents.c_str() + request->offset, + request->file_contents.length() - request->offset, callback); + + // Handle cleanup in the callback if error + if (rv != PP_OK_COMPLETIONPENDING) { + callback.Run(rv); + return; + } + } + } + + void SaveFlushCallback(int32_t result, FileIoInstance::Request* request) { + if (result != PP_OK) { + ShowErrorMessage("File fail to flush", result); + delete request; + return; + } + ShowStatusMessage("Save successful"); + delete request; + } + + bool Load(const std::string& file_name) { + if (!file_system_ready_) { + ShowErrorMessage("File system is not open", PP_ERROR_FAILED); + return false; + } + + FileIoInstance::Request* request = new FileIoInstance::Request; + request->ref = pp::FileRef(file_system_, file_name.c_str()); + request->file = pp::FileIO(this); + + pp::CompletionCallback callback = callback_factory_.NewCallback( + &FileIoInstance::LoadOpenCallback, request); + int32_t rv = request->file.Open(request->ref, PP_FILEOPENFLAG_READ, + callback); + + // Handle cleanup in the callback if error + if (rv != PP_OK_COMPLETIONPENDING) { + callback.Run(rv); + return false; + } + return true; + } + + void LoadOpenCallback(int32_t result, FileIoInstance::Request* request) { + if (result == PP_ERROR_FILENOTFOUND) { + ShowStatusMessage("File not found"); + delete request; + return; + } else if (result != PP_OK) { + ShowErrorMessage("File open for read failed", result); + delete request; + return; + } + + pp::CompletionCallback callback = callback_factory_.NewCallback( + &FileIoInstance::LoadQueryCallback, request); + int32_t rv = request->file.Query(&request->info, callback); + + // Handle cleanup in the callback if error + if (rv != PP_OK_COMPLETIONPENDING) { + callback.Run(rv); + return; + } + } + + void LoadQueryCallback(int32_t result, FileIoInstance::Request* request) { + if (result != PP_OK) { + ShowErrorMessage("File query failed", result); + delete request; + return; + } + + // FileIO.Read() can only handle int32 sizes + if (request->info.size > INT32_MAX) { + ShowErrorMessage("File too big", PP_ERROR_FILETOOBIG); + delete request; + return; + } + + // Allocate a buffer to read the file into + // Here we must allocate on the heap so FileIO::Read will write to this + // one and only copy of file_contents + request->file_contents.resize(request->info.size, '\0'); + request->offset = 0; + + pp::CompletionCallback callback = callback_factory_.NewCallback( + &FileIoInstance::LoadReadCallback, request); + int32_t rv = request->file.Read(request->offset, + &request->file_contents[request->offset], + request->file_contents.length(), callback); + + // Handle cleanup in the callback if error + if (rv != PP_OK_COMPLETIONPENDING) { + callback.Run(rv); + return; + } + } + + void LoadReadCallback(int32_t bytes_read, FileIoInstance::Request* request) { + // If bytes_read < 0 then it indicates the error code + if (bytes_read < 0) { + ShowErrorMessage("File read failed", bytes_read); + delete request; + return; + } + + // Ensure the content length is something read() can handle + assert(request->file_contents.length() <= INT32_MAX); + + request->offset += bytes_read; + + if (request->offset == request->file_contents.length() || bytes_read == 0) { + // Done reading, send content to the user interface + PostMessage(pp::Var("DISP|" + request->file_contents)); + ShowStatusMessage("Load complete"); + delete request; + } else { + // Some bytes remain to be read, call read again with remainder + pp::CompletionCallback callback = callback_factory_.NewCallback( + &FileIoInstance::LoadReadCallback, request); + int32_t rv = request->file.Read(request->offset, + &request->file_contents[request->offset], + request->file_contents.length() - request->offset, callback); + + // Handle cleanup in the callback if error + if (rv != PP_OK_COMPLETIONPENDING) { + callback.Run(rv); + return; + } + } + } + + bool Delete(const std::string& file_name) { + if (!file_system_ready_) { + ShowErrorMessage("File system is not open", PP_ERROR_FAILED); + return false; + } + + FileIoInstance::Request* request = new FileIoInstance::Request; + request->ref = pp::FileRef(file_system_, file_name.c_str()); + + pp::CompletionCallback callback = callback_factory_.NewCallback( + &FileIoInstance::DeleteCallback, request); + int32_t rv = request->ref.Delete(callback); + + // Handle cleanup in the callback if error + if (rv != PP_OK_COMPLETIONPENDING) { + callback.Run(rv); + return false; + } + return true; + } + + void DeleteCallback(int32_t result, FileIoInstance::Request* request) { + if (result == PP_ERROR_FILENOTFOUND) { + ShowStatusMessage("File not found"); + delete request; + return; + } else if (result != PP_OK) { + ShowErrorMessage("Deletion failed", result); + delete request; + return; + } + + ShowStatusMessage("File deleted"); + delete request; + } + + void FileSystemOpenCallback(int32_t result) { + if (result != PP_OK) { + ShowErrorMessage("File system open call failed", result); + return; + } + + file_system_ready_ = true; + // Notify the user interface that we're ready + PostMessage(pp::Var("READY|")); + } + + /// Encapsulates our simple javascript communication protocol + void ShowErrorMessage(const std::string& message, int32_t result) { + std::stringstream ss; + ss << "ERR|" << message << " -- Error #: " << result; + PostMessage(pp::Var(ss.str())); + } + + /// Encapsulates our simple javascript communication protocol + void ShowStatusMessage(const std::string& message) { + std::stringstream ss; + ss << "STAT|" << message; + PostMessage(pp::Var(ss.str())); + } +}; + +/// The Module class. The browser calls the CreateInstance() method to create +/// an instance of your NaCl module on the web page. The browser creates a new +/// instance for each <embed> tag with type="application/x-nacl". +class FileIoModule : public pp::Module { + public: + FileIoModule() : pp::Module() {} + virtual ~FileIoModule() {} + + /// Create and return a FileIoInstance object. + /// @param[in] instance The browser-side instance. + /// @return the plugin-side instance. + virtual pp::Instance* CreateInstance(PP_Instance instance) { + return new FileIoInstance(instance); + } +}; + +namespace pp { +/// Factory function called by the browser when the module is first loaded. +/// The browser keeps a singleton of this module. It calls the +/// CreateInstance() method on the object you return to make instances. There +/// is one instance per <embed> tag on the page. This is the main binding +/// point for your NaCl module with the browser. +Module* CreateModule() { + return new FileIoModule(); +} +} // namespace pp + diff --git a/native_client_sdk/src/examples/file_io/file_io.html b/native_client_sdk/src/examples/file_io/file_io.html new file mode 100644 index 0000000..009c2e8 --- /dev/null +++ b/native_client_sdk/src/examples/file_io/file_io.html @@ -0,0 +1,171 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html> + <!-- + Copyright (c) 2012 The Chromium Authors. All rights reserved. + Use of this source code is governed by a BSD-style license that can be + found in the LICENSE file. + --> +<head> + <title>File I/O Example</title> + + <script type="text/javascript"> + FileIoModule = null; // Global application object. + statusText = 'NO-STATUS'; + + // Request file system space and indicate successful load + function moduleDidLoad() { + FileIoModule = document.getElementById('file_io'); + } + + function createNaClModule() { + // Dynamically generate this HTML: + // <embed name="nacl_module" + // id="file_io" + // width=0 height=0 + // src="file_io.nmf" + // type="application/x-nacl" /> + var listenerDiv = document.getElementById('listener'); + var naclModule = document.createElement('embed'); + naclModule.setAttribute('name', 'nacl_module'); + naclModule.setAttribute('id', 'file_io'); + naclModule.setAttribute('width', 0); + naclModule.setAttribute('height', 0); + naclModule.setAttribute('src', 'file_io.nmf'); + naclModule.setAttribute('type', 'application/x-nacl'); + listenerDiv.appendChild(naclModule); + } + + // If the page loads before the Native Client module loads, then set the + // status message indicating that the module is still loading. Otherwise, + // do not change the status message. + function pageDidLoad() { + // Request file system space + updateStatus('Allocating storage...'); + window.webkitStorageInfo.requestQuota(window.PERSISTENT, 1024*1024, + function(bytes) { + updateStatus('Allocated '+bytes+' bytes of persistant storage.'); + createNaClModule(); + }, + function(e) { alert('Failed to allocate space') }); + } + + // Set the global status message. If the element with id 'statusField' + // exists, then set its HTML to the status message as well. + // opt_message The message test. If this is null or undefined, then + // attempt to set the element with id 'statusField' to the value of + // |statusText|. + function updateStatus(opt_message) { + if (opt_message) + statusText = opt_message; + var statusField = document.getElementById('status_field'); + if (statusField) { + statusField.innerHTML = statusText; + statusField.style.color = "black"; + } + } + + function loadFile() { + if (FileIoModule) { + var fileName = document.getElementById('file_name').value; + + // Package a message using a simple protocol containing: + // instruction file_name_length file_name + var msg = "ld " + fileName.length + " " + fileName; + FileIoModule.postMessage(msg); + } + } + + function saveFile() { + if (FileIoModule) { + var fileName = document.getElementById('file_name').value; + var fileText = document.getElementById('file_editor').value; + + // Package a message using a simple protocol containing: + // instruction file_name_length file_name file_contents + var msg = "sv " + fileName.length + " " + fileName + " " + fileText; + FileIoModule.postMessage(msg); + } + } + + function deleteFile() { + if (FileIoModule) { + var fileName = document.getElementById('file_name').value; + + // Package a message using a simple protocol containing: + // instruction file_name_length file_name + var msg = "de " + fileName.length + " " + fileName; + FileIoModule.postMessage(msg); + } + } + + function handleMessage(message_event) { + var messageParts = message_event.data.split("|", 3); + + if (messageParts[0] == "ERR") { + updateStatus(messageParts[1]); + document.getElementById('status_field').style.color = "red"; + } + else if(messageParts[0] == "STAT") { + updateStatus(messageParts[1]); + } + else if (messageParts[0] == "DISP") { + // Display the message in the file edit box + document.getElementById('file_editor').value = messageParts[1]; + } + else if (messageParts[0] == "READY") { + var statusField = document.getElementById('status_field'); + updateStatus(statusField.innerHTML + ' Ready!'); + } + } + + + </script> +</head> +<body onload="pageDidLoad()"> + +<h1>File I/O example</h1> +This example demonstrates the use of the persistent file system +<p> +The window below allows creating and editing files in the file store. +<p> + <!-- Load the published .nexe. This includes the 'nacl' attribute which + shows how to load multi-architecture modules. Each entry in the "nexes" + object in the .nmf manifest file is a key-value pair: the key is the + instruction set architecture ('x86-32', 'x86-64', etc.); the value is a URL + for the desired NaCl module. + To load the debug versions of your .nexes, set the 'nacl' attribute to the + _dbg.nmf version of the manifest file. + + Note: The <EMBED> element is wrapped inside a <DIV>, which has both a 'load' + and a 'message' event listener attached. This wrapping method is used + instead of attaching the event listeners directly to the <EMBED> element + to ensure that the listeners are active before the NaCl module 'load' + event fires. + --> + <div id="listener"> + <script type="text/javascript"> + var listener = document.getElementById('listener'); + listener.addEventListener('load', moduleDidLoad, true); + listener.addEventListener('message', handleMessage, true); + </script> + + <textarea id="file_editor" + cols="40" + rows="10" + wrap="hard" + placeholder="Enter some text to save in a file..."></textarea> + <br>File Name + <input type="text" id="file_name" action=""/> + <button id="save_but" onclick="saveFile()" action="">Save</button> + <button id="load_but" onclick="loadFile()" action="">Load</button> + <button id="delete_but" onclick="deleteFile()" action="">Delete</button> + </div> +</p> + + + +<h2>Status</h2> +<div id="status_field">NO-STATUS</div> +</body> +</html> diff --git a/native_client_sdk/src/examples/file_io/file_io.nmf b/native_client_sdk/src/examples/file_io/file_io.nmf new file mode 100644 index 0000000..4864849 --- /dev/null +++ b/native_client_sdk/src/examples/file_io/file_io.nmf @@ -0,0 +1,6 @@ +{ + "program": { + "x86-64": {"url": "file_io_x86_64.nexe"}, + "x86-32": {"url": "file_io_x86_32.nexe"} + } +} diff --git a/native_client_sdk/src/examples/index.html b/native_client_sdk/src/examples/index.html index 34aa471..c400f42 100644 --- a/native_client_sdk/src/examples/index.html +++ b/native_client_sdk/src/examples/index.html @@ -76,7 +76,7 @@ See the link for further information. <h3>Common APIs</h3> <dd><p>The following set of examples illustrate various Pepper APIs including -audio, 2D, 3D, input and urls.</p></dd> +audio, 2D, 3D, file I/O, input and urls.</p></dd> <dt><a href="sine_synth/sine_synth.html">Sine Wave Synthesizer</a></dt> <dd> The Sine Wave Synthesizer example demonstrates playing sound (a sine wave). Enter the desired frequency and hit play to start, stop to end. The @@ -113,6 +113,13 @@ audio, 2D, 3D, input and urls.</p></dd> <p>Teaching focus: URL loading.</p> </dd> + + <dt><a href="file_io/file_io.html">File I/O</a></dt> + <dd> The File IO example demonstrates saving, loading, and deleting files + from the persistent file store. + + <p>Teaching focus: File input and output.</p> + </dd> <h3>Common Concepts</h3> <dd><p>The following set of examples illustrate various common concepts such as |