1 ###############################################################################
3 # Package: NaturalDocs::BinaryFile
5 ###############################################################################
7 # A package to manage Natural Docs' binary data files.
11 # - Only one data file can be managed with this package at a time. You must close the file before opening another
14 ###############################################################################
16 # This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
17 # Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
18 # Refer to License.txt for the complete details
23 package NaturalDocs::BinaryFile;
25 use vars qw(@EXPORT @ISA);
29 @EXPORT = ('BINARY_FORMAT');
31 use Encode qw(encode_utf8 decode_utf8);
34 ###############################################################################
38 # Topic: Standard Header
40 # > [UInt8: BINARY_FORMAT]
41 # > [VersionInt: app version]
43 # The first byte is <BINARY_FORMAT>, which distinguishes binary configuration files from text ones, since Natural Docs
44 # used to use text data files with the same name.
46 # The next section is the version of Natural Docs that wrote the file, as defined by <NaturalDocs::Settings->AppVersion>
47 # and written by <NaturalDocs::Version->ToBinaryFile()>.
53 # All the integer data types are written most significant byte first, aka big endian.
55 # An AString16 is a UInt16 followed by that many 8-bit ASCII characters. It doesn't include a null character at the end. Undef
56 # strings are represented by a zero for the UInt16 and nothing following it.
58 # A UString16 is a UInt16 followed by that many UTF-8 encoded bytes. It doesn't include a null character at the end. Undef
59 # strings are represented by a zero for the UInt16 and nothing following it.
63 # Constant: BINARY_FORMAT
65 # An 8-bit constant that's used as the first byte of binary data files. This is used so that you can easily distinguish between
66 # binary and old-style text data files. It's not a character that would appear in plain text files.
68 use constant BINARY_FORMAT => pack('C', 0x06);
69 # Which is ACK or acknowledge in ASCII. Is the cool spade character in DOS displays.
72 ###############################################################################
76 # handle: FH_BINARYDATAFILE
78 # The file handle used for the data file.
85 # The <FileName> for the current configuration file being parsed.
91 ###############################################################################
92 # Group: File Functions
96 # Function: OpenForReading
98 # Opens a binary file for reading.
102 # minimumVersion - The minimum version of the file format that is acceptible. May be undef.
106 # The format <VersionInt> or undef if it failed. It could fail for any of the following reasons.
108 # - The file doesn't exist.
109 # - The file couldn't be opened.
110 # - The file didn't have the proper header.
111 # - Either the application or the file was from a development release, and they're not the exact same development release.
112 # - The file's format was less than the minimum version, if one was defined.
113 # - The file was from a later application version than the current.
115 sub OpenForReading #(FileName file, optional VersionInt minimumVersion) => VersionInt
117 my ($self, $file, $minimumVersion) = @_;
119 if (defined $currentFile)
120 { die "Tried to open binary file " . $file . " for reading when " . $currentFile . " was already open."; };
122 $currentFile = $file;
124 if (open(FH_BINARYDATAFILE, '<' . $currentFile))
126 # See if it's binary.
127 binmode(FH_BINARYDATAFILE);
130 read(FH_BINARYDATAFILE, $firstChar, 1);
132 if ($firstChar == ::BINARY_FORMAT())
134 my $version = NaturalDocs::Version->FromBinaryFile(\*FH_BINARYDATAFILE);
136 if (NaturalDocs::Version->CheckFileFormat($version, $minimumVersion))
137 { return $version; };
140 close(FH_BINARYDATAFILE);
143 $currentFile = undef;
149 # Function: OpenForWriting
151 # Opens a binary file for writing and writes the standard header. Dies if the file cannot be opened.
153 sub OpenForWriting #(FileName file)
155 my ($self, $file) = @_;
157 if (defined $currentFile)
158 { die "Tried to open binary file " . $file . " for writing when " . $currentFile . " was already open."; };
160 $currentFile = $file;
162 open (FH_BINARYDATAFILE, '>' . $currentFile)
163 or die "Couldn't save " . $file . ".\n";
165 binmode(FH_BINARYDATAFILE);
167 print FH_BINARYDATAFILE '' . ::BINARY_FORMAT();
168 NaturalDocs::Version->ToBinaryFile(\*FH_BINARYDATAFILE, NaturalDocs::Settings->AppVersion());
175 # Closes the current configuration file.
182 { die "Tried to close a binary file when one wasn't open."; };
184 close(FH_BINARYDATAFILE);
185 $currentFile = undef;
190 ###############################################################################
191 # Group: Reading Functions
196 # Reads and returns a UInt8 from the open file.
198 sub GetUInt8 # => UInt8
201 read(FH_BINARYDATAFILE, $raw, 1);
203 return unpack('C', $raw);
207 # Function: GetUInt16
208 # Reads and returns a UInt16 from the open file.
210 sub GetUInt16 # => UInt16
213 read(FH_BINARYDATAFILE, $raw, 2);
215 return unpack('n', $raw);
219 # Function: GetUInt32
220 # Reads and returns a UInt32 from the open file.
222 sub GetUInt32 # => UInt32
225 read(FH_BINARYDATAFILE, $raw, 4);
227 return unpack('N', $raw);
231 # Function: GetAString16
232 # Reads and returns an AString16 from the open file. Supports undef strings.
234 sub GetAString16 # => string
237 read(FH_BINARYDATAFILE, $rawLength, 2);
238 my $length = unpack('n', $rawLength);
244 read(FH_BINARYDATAFILE, $string, $length);
250 # Function: GetUString16
251 # Reads and returns a UString16 from the open file. Supports undef strings.
253 sub GetUString16 # => string
256 read(FH_BINARYDATAFILE, $rawLength, 2);
257 my $length = unpack('n', $rawLength);
263 read(FH_BINARYDATAFILE, $string, $length);
264 $string = decode_utf8($string);
271 ###############################################################################
272 # Group: Writing Functions
276 # Function: WriteUInt8
277 # Writes a UInt8 to the open file.
279 sub WriteUInt8 #(UInt8 value)
281 my ($self, $value) = @_;
282 print FH_BINARYDATAFILE pack('C', $value);
286 # Function: WriteUInt16
287 # Writes a UInt32 to the open file.
289 sub WriteUInt16 #(UInt16 value)
291 my ($self, $value) = @_;
292 print FH_BINARYDATAFILE pack('n', $value);
296 # Function: WriteUInt32
297 # Writes a UInt32 to the open file.
299 sub WriteUInt32 #(UInt32 value)
301 my ($self, $value) = @_;
302 print FH_BINARYDATAFILE pack('N', $value);
306 # Function: WriteAString16
307 # Writes an AString16 to the open file. Supports undef strings.
309 sub WriteAString16 #(string value)
311 my ($self, $string) = @_;
314 { print FH_BINARYDATAFILE pack('nA*', length($string), $string); }
316 { print FH_BINARYDATAFILE pack('n', 0); };
320 # Function: WriteUString16
321 # Writes an UString16 to the open file. Supports undef strings.
323 sub WriteUString16 #(string value)
325 my ($self, $string) = @_;
329 $string = encode_utf8($string);
330 print FH_BINARYDATAFILE pack('na*', length($string), $string);
333 { print FH_BINARYDATAFILE pack('n', 0); };