Writing Binary Files with FileIO
July 2, 2002
by David Pollock
In my previous article, I discussed how to use the FileIO Xtra to read data stored in non-text files on your hard drive. This article will focus on the next step, using FileIO to edit portions of existing files, like MP3 files, or creating new files, like bitmaps, from a Director cast member.
Editing MP3 Files
The simplest example of writing to a binary file is to edit the tags in an MP3 file. It's simpler than creating a file from scratch because you don't need to understand nearly as much about the entire structure of the file, you only need to know that the song information is stored in the last 128 bytes of the file. The handler I've made to do the actual FileIO functions looks like this:
on writeBinaryFile filePath, byteList, fileStartByte, listStartByte, deleteOriginalFile
result = 0
fileObj = new (xtra "fileIO")
if deleteOriginalFile then
-- It's often "cleaner" to remove the existing file
-- though you obviously wouldn't do this when editing
-- an MP3 file
fileObj.openFile (filePath, 0)
fileObj.delete ()
fileObj.createFile (filePath)
end if
fileObj.openFile (filePath, 2)
-- Attempt to open the file with write-only access
if fileObj.status () = 0 then
listLength = byteList.count
if fileStartByte.voidP then fileStartByte = 1
-- Set default positions if none were supplied via
-- the fileStartByte & listStartByte parameters
if listStartByte.voidP then listStartByte = 1
fileObj.setPosition (fileStartByte-1)
-- Set the write position in the file & begin dumping
-- the list contents into the file
repeat with index = listStartByte to listLength
fileObj.writeChar (numToChar (byteList[index]))
end repeat
result = 1
end if
fileObj.closeFile ()
fileObj = 0
return result
end
Here are explanations of the parameters:
filePath | The full path to file you want to alter or create. |
byteList | A linear list containing values ranging from 0 to 255. For ASCII text, this would normally be between 32 and 126. This website (http://www.asciitable.com/) has an extensive list of the ASCII character values. For other types of binary data, things get a little more complex, so we'll just focus on saving text for now. |
fileStartByte | Used to indicate where in the existing file to begin writing data (for non-existant files, the fileObj.setPosition() function would just be ignored). |
listStartByte | Allows you to specify a position in the byteList to start from if not all of your data needs to be stored. This is useful if, for example, you know that only a portion of an image's data has been altered. |
deleteOriginalFile | Allows you to cleanly replace an existing file by first removing it from the file system. |
The example mp3_editor.dir movie demonstrates how the writeBinaryFile handler is used, along with a few other handlers for formatting the MP3 tag information prior to writing data into the MP3 file. It should provide you with a good example of how to read from and save to a segment of a file on disk. One thing to remember, though, is that all of the information being read and written is still basically ASCII text stored at the end of a binary file. Which brings us to the concept of data types.
Data Types
Data inside a file can represent many different types of values: booleans, integers, floating point numbers, and text, among others. And, as opposed to Lingo, which supports only integer and floating point numeric data types, most other languages support multiple formats of integers and floating point numbers, some of which are described below. In order to make use of the data in a file, we first need to know what kind of data we're dealing with, and where the data is located in the file. Knowing what the data type is allows us to convert it into a corresponding data type in Lingo.
Looking at the code of the mp3_editor.dir movie, notice the handlers named getByteValue and appendByteData. These are used for data conversion. The getByteValue handler converts byte data retrieved from a file into a value that can be used in Lingo. Conversely, appendByteData converts Lingo variables into byte values for use when saving data back into a file. Currently, only ASCII and 8-, 16-, and 32-bit unsigned integer data types are supported by the getByteValue and appendByteData handlers, for the simple reason that these are the only types I have needed thus far, but any number of other data types could be added by modifying these scripts.
Definitions of a few data types:
String | ASCII text, 8 bits (one byte) per character. |
Unsigned integers (8, 16, 32, 64 bits) |
Unsigned indicates that these values can only be positive integers, whose range is dependent on the number of bits used. A 16-bit integer, then, would have a range between 0 and 65,535 (2^16 - 1). |
Signed integers (8, 16, 32, 64 bits) |
Signed indicates that these values can be positive or negative. As with unsigned integers, the range of numbers they can represent is dependent on the numbers of bits used, the difference being that half the numbers are now negative. So, a 16-bit signed integer will range from –32,768 to 32,767. |
Saving Bitmaps With FileIO
The bitmap_export.dir movie gives you an example of creating a file from a cast member's image data. Again, using information found at the Programmers File Format Collection, we can determine the file layout for bitmap image files. The trickiest part is making sure the file header information is compiled correctly. This is where knowledge of the different numeric data types mentioned in the last section comes into play, because the bitmap format makes use of 8, 16 and 32-bit unsigned integer numbers, as well as ASCII text data, for various properties stored in the file.
As with the data conversion handlers, you can add more complexity to the exportBitmap handler inside the bitmap_export.dir movie to add support for other features of the bitmap format. These include creating images with palettes, using run length encoding for image compression, and saving images of different bit depths, among others.
Other File Formats
The types of files that you can now pry into or save via FileIO is as varied as the number of file types in existance. This includes audio, bitmaps, database files, digital movies and basically anything else that you can copy to a hard drive. Now, I'm not saying anyone would want to build a QuickTime editor in Director, or try to re-create Debabelizer, but for small tasks where speed isn't an issue, FileIO works, and it's cheap.
Both of the sample movies for this article are available in Director 8.5 format as ZIP or SIT archives.
All colorized Lingo code samples have been processed by Dave Mennenoh's brilliant HTMLingo Xtra, available from his site at http://www.crackconspiracy.com/~davem/
Copyright 1997-2024, Director Online. Article content copyright by respective authors.