Io (lx-io.hpp)
Contents
- 1 General I/O
- 1.1 (1) SDK: LXe_IO_ERROR, etc. defines
- 1.2 Block Stream Interfaces
- 1.2.1 (2) SDK: LXu_BLOCKREAD, etc. defines
- 1.2.2 (3) SDK: LXtBlockHeader struct
- 1.2.3 (4) SDK: LXfBLKS_FORCE, etc. defines
- 1.2.4 (5) SDK: BlockRead::FindBlock, etc.
- 1.2.5 (6) SDK: Declarations
- 1.2.6 (7) SDK: BlockRead::ReadString
- 1.2.7 (8) SDK: LXfREADSTRING_FORCE, etc. defines
- 1.2.8 (9) SDK: BlockRead::ReadIDCode, etc.
- 1.2.9 (10) SDK: BlockRead::SetSourceEncoding, etc.
- 1.2.10 (11) SDK: CLxUser_BlockRead::Read method
- 1.2.11 (12) SDK: CLxUser_BlockRead::Read method
- 1.2.12 (13) PY: empty BlockRead user class
- 1.2.13 (14) SDK: LXfBLKW_LEAF, etc. defines
- 1.2.14 (15) SDK: BlockWrite::WriteBlock, etc.
- 1.2.15 (16) SDK: BlockWrite::SetSourceEncoding, etc.
- 1.2.16 (17) SDK: CLxUser_BlockWrite::Write method
- 1.2.17 (18) PY: empty BlockWrite user class
- 1.3 Loaders
- 1.3.1 (19) SDK: Loader::Recognize
- 1.3.2 (20) SDK: LoaderInfo::TestClass
- 1.3.3 (21) SDK: LoaderInfo::SetClass, etc.
- 1.3.4 (22) SDK: LXfLOAD_OPAQUE, etc. defines
- 1.3.5 (23) SDK: Loader::LoadInstance, etc.
- 1.3.6 (24) SDK: Loader::Cleanup
- 1.3.7 (25) SDK: Loader::SpawnOptions
- 1.3.8 (26) SDK: LXu_LOADERINFO, etc. defines
- 1.3.9 (27) SDK: LXsLOD_CLASSLIST, etc. defines
- 1.3.10 (28) SDK: empty LoaderInfo User Class
- 1.3.11 (29) SDK: empty Loader User Class
- 1.3.12 (30) PY: empty LoaderInfo user class
- 1.3.13 (31) PY: empty Loader user class
- 1.3.14 (32) SDK: LXu_SIDEEFFECT, etc. defines
- 1.4 Saver
- 1.5 Monitors
- 1.6 Object I/O
General I/O
These interfaces provide a set of common objects for doing all types of input and output. Raw streams provide a low-level way of performing serial I/O on a variety of different substrates. Block streams add more structure to simple linear I/O, allowing data to be more formally defined. IFF format streams are a special case of block format, supporting a more rigid structure and larger overall size.
Loaders and savers define the extensible architecture for importing and exporting objects of all types. Objects may be loaded opaquely so they are implemented externally, or transparently so they are implemented by built-in nexus objects.
These are error codes for this subsystem and all other I/O-related subsystems.
(1) SDK: LXe_IO_ERROR, etc. defines
#define LXe_IO_ERROR LXxFAILCODE(LXeSYS_IO,1) #define LXe_REF_BADID LXxFAILCODE(LXeSYS_IO,22) #define LXe_REF_BADPTR LXxFAILCODE(LXeSYS_IO,23) #define LXe_REF_DUPLICATE LXxFAILCODE(LXeSYS_IO,24) #define LXe_IO_SHORTREAD LXxFAILCODE(LXeSYS_IO,25) #define LXe_IO_NEEDGL LXxFAILCODE(LXeSYS_IO,26) #define LXe_IO_TRUNCATED LXxGOODCODE(LXeSYS_IO,27) #define LXe_IO_PARTIALSTRING LXxGOODCODE(LXeSYS_IO,28)
Block Stream Interfaces
Block stream interfaces support block-structured I/O with multiple data types. There is an interface for reading and one for writing.
(2) SDK: LXu_BLOCKREAD, etc. defines
#define LXu_BLOCKREAD "36A00A47-2664-49E0-BAFF-263E638532B5" #define LXu_BLOCKWRITE "ADE569E1-E9A1-4AD6-B625-634093660965"
Block streams are also style-transparent, reading and writing using the same API for both text and binary streams. As result, blocks have to be identified by both a binary ID code and a text string. Internally, the binary ID code value is always used.
(3) SDK: LXtBlockHeader struct
LXtID4 id; const char *token;
For string reading, there are two modes - FORCE and RAW. Force will raise an exception if the string is not found. If not set, however, the function will return true if any string is read, including the empty string. RAW causes the stream to be read as a literal string, ignoring quotes and reading to eol.
(4) SDK: LXfBLKS_FORCE, etc. defines
#define LXfBLKS_FORCE (1<<0) #define LXfBLKS_RAW (1<<1)
Function descriptions are not provided since these objects are not commonly encountered in nexus file I/O. TODO: fill these in.
(5) SDK: BlockRead::FindBlock, etc.
LXxMETHOD( LxResult, FindBlock) ( LXtObjectID self, const LXtBlockHeader *head, int flags, LXtID4 *blkId); LXxMETHOD( void, End) ( LXtObjectID self); LXxMETHOD( int, Depth) ( LXtObjectID self); LXxMETHOD( LxResult, ReadI1) ( LXtObjectID self, char *data, int count, int *ocount); LXxMETHOD( LxResult, ReadI2) ( LXtObjectID self, short *data, int count, int *ocount); LXxMETHOD( LxResult, ReadI4) ( LXtObjectID self, int *data, int count, int *ocount); LXxMETHOD( LxResult, ReadU1) ( LXtObjectID self, unsigned char *data, int count, int *ocount); LXxMETHOD( LxResult, ReadU2) ( LXtObjectID self, unsigned short *data, int count, int *ocount); LXxMETHOD( LxResult, ReadU4) ( LXtObjectID self, unsigned int *data, int count, int *ocount); LXxMETHOD( LxResult, ReadFP) ( LXtObjectID self, float *data, int count, int *ocount); LXxMETHOD( LxResult, ReadFP8) ( LXtObjectID self, double *data, int count, int *ocount);
(6) SDK: Declarations
Reading a string takes a buffer for the result and its size. If FORCE is true this will return an error if no string is found. If PARTIAL is true then as much string as can be found will be read into the buffer and LXe_IO_PARTIALSTRING will be returned if there is more left to read. Otherwise the entire string will always be read and LXe_IO_TRUNCATED will be returned if some of the string had to be discarded. The returned count is the number of characters read including the terminating null.
(7) SDK: BlockRead::ReadString
LXxMETHOD( LxResult, ReadString) ( LXtObjectID self, char *buf, int max, int flags, int *ocount);
(8) SDK: LXfREADSTRING_FORCE, etc. defines
#define LXfREADSTRING_FORCE 0x01 #define LXfREADSTRING_RAW 0x02 #define LXfREADSTRING_PARTIAL 0x04
(9) SDK: BlockRead::ReadIDCode, etc.
LXxMETHOD( LxResult, ReadIDCode) ( LXtObjectID self, const LXtBlockHeader *list, LXtID4 *idCode); LXxMETHOD( LxResult, ReadVX) ( LXtObjectID self, unsigned *data, int count, int *ocount);
ReadString converts a text encoding of string to another text encoding given by the following methods using ILxTextEncoding service. The source encoding is the encoding for strings to read. The target encoding is for a string set to "buf" of ReadString. The text encoding convertion does not work with PARTIAL mode. The returned count is the number of characters read including the terminating null based on the source string.
(10) SDK: BlockRead::SetSourceEncoding, etc.
LXxMETHOD( LxResult, SetSourceEncoding) ( LXtObjectID self, unsigned encoding); LXxMETHOD( LxResult, SetTargetEncoding) ( LXtObjectID self, unsigned encoding);
User class methods are relatively more C++ friendly, using argument types and throwing exceptions on errors. The read count is discarded for these methods because they require reading.
(11) SDK: CLxUser_BlockRead::Read method
bool Read ( short *val, bool force = true) { int count; LxResult rc; rc = ReadI2 (val, (force ? 1 : -1), &count); if (LXx_FAIL (rc)) throw (rc); return (count == 1); } bool Read ( unsigned short *val, bool force = true) { int count; LxResult rc; rc = ReadU2 (val, (force ? 1 : -1), &count); if (LXx_FAIL (rc)) throw (rc); return (count == 1); } bool Read ( int *val, bool force = true) { int count; LxResult rc; rc = ReadI4 (val, (force ? 1 : -1), &count); if (LXx_FAIL (rc)) throw (rc); return (count == 1); } bool Read ( unsigned int *val, bool force = true) { int count; LxResult rc; rc = ReadU4 (val, (force ? 1 : -1), &count); if (LXx_FAIL (rc)) throw (rc); return (count == 1); } bool Read ( float *val, bool force = true) { int count; LxResult rc; rc = ReadFP (val, (force ? 1 : -1), &count); if (LXx_FAIL (rc)) throw (rc); return (count == 1); } bool Read ( double *val, bool force = true) { int count; LxResult rc; rc = ReadFP8 (val, (force ? 1 : -1), &count); if (LXx_FAIL (rc)) throw (rc); return (count == 1); }
Reading strings in C++ does partial reads to build up an STL string. If force is true it will raise an exception if the string is not found. Otherwise no string and a valid empty string will both return false. The terminal string has a final null, which we don't want included in the STL string.
(12) SDK: CLxUser_BlockRead::Read method
bool Read ( std::string &result, bool force = true) { char buf[128]; LxResult rc; int flag, got; result = ""; flag = LXfREADSTRING_PARTIAL | (force ? LXfREADSTRING_FORCE : 0); while (1) { rc = ReadString (buf, sizeof (buf), flag, &got); if (LXx_FAIL (rc)) throw (rc); if (rc == LXe_IO_PARTIALSTRING) result.append (buf, (size_t) got); else { if (got) result.append (buf, (size_t) got - 1); break; } } return !result.empty (); }
Empty BlockRead Python user class.
(13) PY: empty BlockRead user class
pass
The write methods are very similar to the read methods. Function descriptions are not provided since these objects are not commonly encountered in nexus file I/O. TODO: fill these in.
LEAF blocks can contain no sub-blocks. PARAM blocks are leaf blocks written at the start of a non-leaf block. In XML they are written into the block header, but otherwise are identical to normal blocks.
(14) SDK: LXfBLKW_LEAF, etc. defines
#define LXfBLKW_LEAF (1<<0) #define LXfBLKW_PARAM (1<<1)
(15) SDK: BlockWrite::WriteBlock, etc.
LXxMETHOD( LxResult, WriteBlock) ( LXtObjectID self, const LXtBlockHeader *head, int flags); LXxMETHOD( void, End) ( LXtObjectID self); LXxMETHOD( int, Depth) ( LXtObjectID self); LXxMETHOD( LxResult, WriteI1) ( LXtObjectID self, const char *data, int count); LXxMETHOD( LxResult, WriteI2) ( LXtObjectID self, const short *data, int count); LXxMETHOD( LxResult, WriteI4) ( LXtObjectID self, const int *data, int count); LXxMETHOD( LxResult, WriteU1) ( LXtObjectID self, const unsigned char *data, int count); LXxMETHOD( LxResult, WriteU2) ( LXtObjectID self, const unsigned short *data, int count); LXxMETHOD( LxResult, WriteU4) ( LXtObjectID self, const unsigned int *data, int count); LXxMETHOD( LxResult, WriteFP) ( LXtObjectID self, const float *data, int count); LXxMETHOD( LxResult, WriteFP8) ( LXtObjectID self, const double *data, int count); LXxMETHOD( LxResult, WriteString) ( LXtObjectID self, const char *str); LXxMETHOD( LxResult, WriteIDCode) ( LXtObjectID self, const LXtBlockHeader *ident); LXxMETHOD( LxResult, WriteVX) ( LXtObjectID self, const unsigned int *data, int count);
WriteString converts a text encoding of string to another text encoding given by the following methods using ILxTextEncoding service. The source encoding is for a string set to "buf" of WriteString. The target encoding is the encoding for strings to write.
(16) SDK: BlockWrite::SetSourceEncoding, etc.
LXxMETHOD( LxResult, SetSourceEncoding) ( LXtObjectID self, unsigned encoding); LXxMETHOD( LxResult, SetTargetEncoding) ( LXtObjectID self, unsigned encoding);
User class methods are relatively more C++ friendly, using argument types and throwing exceptions on errors.
(17) SDK: CLxUser_BlockWrite::Write method
void Write ( const short val) { LxResult rc = WriteI2 (&val, 1); if (LXx_FAIL (rc)) throw (rc); } void Write ( const unsigned short val) { LxResult rc = WriteU2 (&val, 1); if (LXx_FAIL (rc)) throw (rc); } void Write ( const int val) { LxResult rc = WriteI4 (&val, 1); if (LXx_FAIL (rc)) throw (rc); } void Write ( const unsigned int val) { LxResult rc = WriteU4 (&val, 1); if (LXx_FAIL (rc)) throw (rc); } void Write ( const float val) { LxResult rc = WriteFP (&val, 1); if (LXx_FAIL (rc)) throw (rc); } void Write ( const double val) { LxResult rc = WriteFP8 (&val, 1); if (LXx_FAIL (rc)) throw (rc); } void Write ( const char *val) { LxResult rc = WriteString (val); if (LXx_FAIL (rc)) throw (rc); } void Write ( const std::string &val) { LxResult rc = WriteString (val.c_str ()); if (LXx_FAIL (rc)) throw (rc); }
Empty BlockWrite Python user class.
(18) PY: empty BlockWrite user class
pass
Loaders
An ILxLoader interface is an extensible server for loading objects of many different classes. Methods on the loader interface are called in sequence. Specifically Recognize() is called to open a file, and Cleanup() is called to close it. Recognize() is given the filename and LoadInfo object, and it should return OK if the file can be loaded by this loader.
(19) SDK: Loader::Recognize
LXxMETHOD( LxResult, Recognize) ( LXtObjectID self, const char *filename, LXtObjectID loadInfo);
The LoaderInfo interface can be used to read the context of the load and to set the attributes that apply to this file. For loaders that can load more than one object type, TestClass() returns LXe_TRUE for classes that the client wants to accept. The priority for each type can be used to decide which one to accept, where lower is better.
(20) SDK: LoaderInfo::TestClass
LXxMETHOD( LxResult, TestClass) ( LXtObjectID self, const LXtGUID *clsGUID, unsigned *priority);
During recognition the loader can set information about a file that it's capable of loading. All of this is optional. The class is the object type for loaders that can load more than one type. Flags can suggest opaque versus direct load and if the format has options. The format can be set if a different saver should be used for this object type.
(21) SDK: LoaderInfo::SetClass, etc.
LXxMETHOD( LxResult, SetClass) ( LXtObjectID self, const LXtGUID *clsGUID); LXxMETHOD( LxResult, SetFlags) ( LXtObjectID self, unsigned flags); LXxMETHOD( LxResult, SetFormat) ( LXtObjectID self, const char *format);
(22) SDK: LXfLOAD_OPAQUE, etc. defines
#define LXfLOAD_OPAQUE 0x01 #define LXfLOAD_OPTIONS 0x02
The loader can also set other data by querying for additional interfaces based on object type. This is useful when doing a direct load to specify the attributes of the object to contain the data. For instance to do a direct load of an image the loader would query for ImageLoaderTarget and use its methods to set the pixel format and size of the destination image. The LoadInfo object can also be queried for a monitor.
After recognition, the LoadInstance() method may be called to load opaque objects, in which case the new COM object should be returned in the void pointer. For non-opaque loads the (perhaps confusingly named) LoadObject() method may be called to load the data into the destination object.
(23) SDK: Loader::LoadInstance, etc.
LXxMETHOD( LxResult, LoadInstance) ( LXtObjectID self, LXtObjectID loadInfo, LXtObjectID monitor, void **ppvObj); LXxMETHOD( LxResult, LoadObject) ( LXtObjectID self, LXtObjectID loadInfo, LXtObjectID monitor, LXtObjectID dest);
After any attempt at recognition Cleanup() will be called to allow the loader to close the file and reset its state.
(24) SDK: Loader::Cleanup
LXxMETHOD( void, Cleanup) ( LXtObjectID self);
This method creates an option object for this loader. This should have a StreamIO interface to allow it to be saved and loaded.
(25) SDK: Loader::SpawnOptions
LXxMETHOD( LxResult, SpawnOptions) ( LXtObjectID self, void **ppvObj);
(26) SDK: LXu_LOADERINFO, etc. defines
#define LXu_LOADERINFO "4CA8BE1A-6ADE-4F93-99F6-1F0EFC8A581E" #define LXa_LOADERINFO "loaderinfo" #define LXu_LOADER "7711F608-B8FF-48BF-81ED-CEBDE54D34DE" #define LXa_LOADER "loader2"
Tags define the classes this loader server supports and hints about the files that it might be able to process.
CLASSLIST | a string of class aliases or GUIDs separated by spaces, such as "image animation". These are the classes which this loader can support. |
DOSPATTERN | a pattern string for filenames in Microsoft Windows (really DOS) format. This is typically something like "*.avi;*.jpg". |
MACPATTERN | a pattern string for Macintosh filetypes that might be read with this loader. This is typically something like "MooV;TTXT". |
THREADSAFE | indicates whether the loader is threadsafe (i.e. multiple loaders of the same type can be executed at once). Values other than "Yes" are all interpreted as no. |
(27) SDK: LXsLOD_CLASSLIST, etc. defines
#define LXsLOD_CLASSLIST "loader.classList" #define LXsLOD_DOSPATTERN "loader.dosPattern" #define LXsLOD_MACPATTERN "loader.macPattern" #define LXsLOD_THREADSAFE "loader.threadsafe"
(28) SDK: empty LoaderInfo User Class
(29) SDK: empty Loader User Class
Empty LoaderInfo Python user class.
(30) PY: empty LoaderInfo user class
pass
Empty Loader Python user class.
(31) PY: empty Loader user class
pass
One general class GUID for loaders is the "side-effect" object class. A loader which loads a side-effect object doesn't really load anything, it just wants a chance to parse a file and do something. There is no ILxSideEffect interface.
(32) SDK: LXu_SIDEEFFECT, etc. defines
#define LXu_SIDEEFFECT "3414D56B-31DE-47C7-B751-092B51591DD2" #define LXa_SIDEEFFECT "sideeffect"
Saver
An ILxSaver interface knows how to save objects of a specific type. This is a very much simpler interface and just has a single function to perform the save to the given file using the given monitor. The Verify method allows the plugin to inform the user if parts of his scene will not be saved. The source is the object being saved.
(33) SDK: Saver::Verify, etc.
LXxMETHOD( LxResult, Verify) ( LXtObjectID self, LXtObjectID source, LXtObjectID message); LXxMETHOD( LxResult, Save) ( LXtObjectID self, LXtObjectID source, const char *filename, LXtObjectID monitor);
(34) SDK: LXu_SAVER, etc. defines
Tags define the single class and file type.
OUTCLASS | the class alias or GUID for objects that can be saved. ("image") |
DOSTYPE | the three-character DOS file extension that should be used by default. ("JPG") |
MACTYPE | the four-character Macintosh type code that should be applied to the new file. ("PICT") |
OVERWRITE | indicates that the saver needs to original file in place in order to save changes into it. This disables any safety mechanism that renames the original file to prevent save errors from destroying data. Savers that set this tag to "1" are expected to handle save errors gracefully on their own. |
SAVEAS | indicates that the saver can be used in the Save As dialog. Save As will allow only savers that have appropriate loader to be listed and it's comparing saver and loader by using server name. If you have named loader and saver for the same file format differently, then you can use this flag in order to get your saver to be displayed in the Save As dialog. |
(35) SDK: LXsSAV_OUTCLASS, etc. defines
#define LXsSAV_OUTCLASS "saver.outClass" #define LXsSAV_DOSTYPE "saver.dosType" #define LXsSAV_MACTYPE "saver.macType" #define LXsSAV_OVERWRITE "saver.overwrite" #define LXsSAV_SAVEAS "saver.saveAs"
(36) SDK: empty Saver User Class
Empty Saver Python user class.
(37) PY: empty Saver user class
pass
Monitors
An ILxMonitor interface allows progress tracking. The monitor is initialized with a max count, and each stage of processing increments the count by something.
Initialize() takes the total number of steps the monitor has, and returns a result code. It is possible that the monitor will be immediately aborted, so you should check for LXe_ABORT codes from this call.
(38) SDK: Monitor::Initialize
LXxMETHOD( LxResult, Initialize) ( LXtObjectID self, unsigned int count);
This steps the monitor by the count provided. Care should be taken to keep from exceeding the total number of steps set with Initialize(). Of the user has aborted the operation, this method will return LXe_ABORT and the client should stop the operation. Commands normally set the LXe_ABORT code on their message object so that the caller knows that the command has been aborted by the user.
(39) SDK: Monitor::Increment
LXxMETHOD( LxResult, Increment) ( LXtObjectID self, unsigned int count);
These macros support calling the function vectors on monitor objects. If the monitor pointer is null, these do no harm. Init returns zero on failure and incr/step returns zero to continue. The Increment() method can be called directly to determine if it returned a failure code or LXe_ABORT.
The most useful macros here are:
INIT() | Returns true if the monitor was successfully initialized. This may return false to indicate that that the monitor was aborted as soon as it was opened, usually because it wound up being a sub-monitor of another monitor. |
INCR() | Increment a monitor by the given number of steps. Returns true if the user aborted, and false if it is still running. |
STEP() | Increment the monitor by one step. Returns true if the user aborted. |
TEST() | Returns true if the user aborted, and false if it is still running. Under the hood, this is simply calling INCR() with a step of 0, and will check for user input before returning. If you are running a long but unbounded task where you can't step the monitor, this will allow you to check for aborts without the app appearing to be locked. |
STATUS() | Same as TEST(), but returns an LxResult code of LXe_ABORT if the user aborted, and LXe_OK if the monitor is still running. |
(40) SDK: LXu_MONITOR, etc. defines
#define LXu_MONITOR "2B514D4C-5142-4687-BCEF-B0FD44A33146" #define LXa_MONITOR "monitor" #define LXxMON_CALL(m,fn,k) ((m[0]->fn)(m,k)) #define LXxMON_OCAL(m,fn,k) (m ? LXxMON_CALL(m,fn,k) : LXe_OK) #define LXxMON_INIT(m,n) LXx_OK (LXxMON_OCAL(m,Initialize,n)) #define LXxMON_INCR(m,i) LXx_FAIL(LXxMON_OCAL(m,Increment, i)) #define LXxMON_STEP(m) LXxMON_INCR(m,1) #define LXxMON_TEST(m) LXxMON_INCR(m,0) #define LXxMON_STATUS(m) LXxMON_OCAL(m,Increment, 0)
The above macros are made more or less obsolete by the user class, which provides simpler methods which do nothing if there is no interface.
(41) SDK: CLxUser_Monitor::Init method
Similar methods for Python.
(42) PY: Monitor.Init method
def Init(self, count): if self.test(): self.Initialize(count) def Step(self): if self.test(): self.Increment(1)
Object I/O
An ILxStreamIO interface allows any object to save or load itself on a block stream. The Write() method writes the underlying object to the block stream, while the Read() method reads the data and sets the state of the object to match the one that was written. The caller will have determined that an object of the right type follows in the stream.
(43) SDK: StreamIO::Write, etc.
LXxMETHOD( LxResult, Write) ( LXtObjectID self, LXtObjectID stream); LXxMETHOD( LxResult, Read) ( LXtObjectID self, LXtObjectID stream);
(44) SDK: LXu_STREAMIO define
#define LXu_STREAMIO "2884D6EE-4BA7-46D6-A776-8EE22C9FD414"
Empty StreamIO Python user class.
(45) PY: empty StreamIO user class
pass