foundation-module: Chapter 6: Nowebify.
This commit is contained in:
parent
82de01cd6f
commit
4eee2a66d4
3 changed files with 64 additions and 64 deletions
|
@ -2,11 +2,11 @@
|
|||
|
||||
Routines for reading raw data from binary files.
|
||||
|
||||
@h Reading binary data.
|
||||
@ \section{Reading binary data.}
|
||||
To begin with, integers of 8, 16, 32 and 64 bit widths respectively,
|
||||
arranged with most significant byte (MSB) first.
|
||||
|
||||
=
|
||||
<<*>>=
|
||||
int BinaryFiles::read_int8(FILE *binary_file, unsigned int *result) {
|
||||
int c1 = getc(binary_file);
|
||||
if (c1 == EOF) return FALSE;
|
||||
|
@ -66,7 +66,7 @@ int BinaryFiles::read_int64(FILE *binary_file, unsigned long long *result) {
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
@ =
|
||||
<<*>>=
|
||||
int BinaryFiles::write_int32(FILE *binary_file, unsigned int val) {
|
||||
int c1 = (int) ((val >> 24) & 0xFF);
|
||||
int c2 = (int) ((val >> 16) & 0xFF);
|
||||
|
@ -83,7 +83,7 @@ int BinaryFiles::write_int32(FILE *binary_file, unsigned int val) {
|
|||
@ We will sometimes need to toggle between MSB and LSB representation of
|
||||
integers 32 or 64 bits wide:
|
||||
|
||||
=
|
||||
<<*>>=
|
||||
void BinaryFiles::swap_bytes32(unsigned int *value) {
|
||||
unsigned int result = (((*value & 0xff) << 24) +
|
||||
((*value & 0xff00) << 8) +
|
||||
|
@ -109,7 +109,7 @@ bytes (most significant first) in which each byte consists of seven bits
|
|||
of data plus a most significant bit which marks that a continuation byte
|
||||
follows:
|
||||
|
||||
=
|
||||
<<*>>=
|
||||
int BinaryFiles::read_variable_length_integer(FILE *binary_file, unsigned int *result) {
|
||||
int c;
|
||||
|
||||
|
@ -126,7 +126,7 @@ int BinaryFiles::read_variable_length_integer(FILE *binary_file, unsigned int *r
|
|||
@ Here we read just the mantissa of a particular representation of
|
||||
floating-point numbers:
|
||||
|
||||
=
|
||||
<<*>>=
|
||||
int BinaryFiles::read_float80(FILE *binary_file, unsigned int *result) {
|
||||
int c1, c2, exp;
|
||||
unsigned int prev = 0, mantissa;
|
||||
|
@ -148,10 +148,10 @@ int BinaryFiles::read_float80(FILE *binary_file, unsigned int *result) {
|
|||
}
|
||||
|
||||
@ And lastly we read a string of a supplied length from the file, and
|
||||
then null terminate it to make it valid C string. (|string| must therefore
|
||||
be at least |length| plus 1 bytes long.)
|
||||
then null terminate it to make it valid C string. ([[string]] must therefore
|
||||
be at least [[length]] plus 1 bytes long.)
|
||||
|
||||
=
|
||||
<<*>>=
|
||||
int BinaryFiles::read_string(FILE *binary_file, char *string, unsigned int length) {
|
||||
if (length > 0) {
|
||||
if (fread(string, 1, length, binary_file) != length) return FALSE;
|
||||
|
@ -161,9 +161,9 @@ int BinaryFiles::read_string(FILE *binary_file, char *string, unsigned int lengt
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
@h Size.
|
||||
@ \section{Size.}
|
||||
|
||||
=
|
||||
<<*>>=
|
||||
long int BinaryFiles::size(filename *F) {
|
||||
FILE *TEST_FILE = BinaryFiles::try_to_open_for_reading(F);
|
||||
if (TEST_FILE) {
|
||||
|
@ -178,9 +178,9 @@ long int BinaryFiles::size(filename *F) {
|
|||
return -1L;
|
||||
}
|
||||
|
||||
@h Opening.
|
||||
@ \section{Opening.}
|
||||
|
||||
=
|
||||
<<*>>=
|
||||
FILE *BinaryFiles::open_for_reading(filename *F) {
|
||||
FILE *handle = Filenames::fopen(F, "rb");
|
||||
if (handle == NULL) Errors::fatal_with_file("unable to read file", F);
|
||||
|
@ -205,11 +205,11 @@ void BinaryFiles::close(FILE *handle) {
|
|||
fclose(handle);
|
||||
}
|
||||
|
||||
@h Copying.
|
||||
@ \section{Copying.}
|
||||
This achieves a binary copy of a file when we haven't got access to the shell,
|
||||
or to system APIs.
|
||||
|
||||
=
|
||||
<<*>>=
|
||||
int BinaryFiles::copy(filename *from, filename *to, int suppress_error) {
|
||||
if ((from == NULL) || (to == NULL))
|
||||
Errors::fatal("files confused in copier");
|
||||
|
@ -237,25 +237,25 @@ int BinaryFiles::copy(filename *from, filename *to, int suppress_error) {
|
|||
return size;
|
||||
}
|
||||
|
||||
@h MD5 hash computation.
|
||||
@ \section{MD5 hash computation.}
|
||||
Though now seen as insecure from a cryptographic point of view, Message Digest 5,
|
||||
a form of checksum created by Ronald Rivest in 1992, remains very useful as a
|
||||
way to compare files quickly, at least when we're sure nobody is being malicious.
|
||||
|
||||
There are thousands of amateur implementations, most of them, like this one,
|
||||
paraphrased from the pseudocode at the Wikipedia page. The |mask| function allows
|
||||
paraphrased from the pseudocode at the Wikipedia page. The [[mask]] function allows
|
||||
certain fixed byte positions in the file to be considered as if they were zero bytes,
|
||||
which is helpful when testing comparing files whose headers change in uninteresting
|
||||
ways. If |mask| is |NULL|, or always returns |FALSE|, then the hash computed is
|
||||
ways. If [[mask]] is [[NULL]], or always returns [[FALSE]], then the hash computed is
|
||||
exactly the canonical md5.
|
||||
|
||||
The code below is about as enigmatic as a page of well-meaning code can be, but
|
||||
that's down to the algorithm itself. The |K| array hold bits drawn from the sines
|
||||
that's down to the algorithm itself. The [[K]] array hold bits drawn from the sines
|
||||
of the integers 1 to 64: sines computed in radians, so that in a sense the md5
|
||||
algorithm relies on the irrationality of $\pi$ to make these so unpredictable.
|
||||
At any rate, the magic numbers below are all drawn from RFC 1321.
|
||||
|
||||
=
|
||||
<<*>>=
|
||||
void BinaryFiles::md5(OUTPUT_STREAM, filename *F, int (*mask)(uint64_t)) {
|
||||
uint32_t s[64] = {
|
||||
7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
|
||||
|
@ -294,36 +294,36 @@ void BinaryFiles::md5(OUTPUT_STREAM, filename *F, int (*mask)(uint64_t)) {
|
|||
uint64_t L = 0;
|
||||
while (BinaryFiles::read_int8(bin, &b)) {
|
||||
if ((mask) && (mask(L))) b = 0;
|
||||
@<Process one byte of message@>;
|
||||
<<Process one byte of message>>;
|
||||
L++;
|
||||
}
|
||||
uint64_t original_length = L*8;
|
||||
|
||||
b = 0x80;
|
||||
@<Process one byte of message@>;
|
||||
<<Process one byte of message>>;
|
||||
L++;
|
||||
while (L % 64 != 56) {
|
||||
b = 0;
|
||||
@<Process one byte of message@>;
|
||||
<<Process one byte of message>>;
|
||||
L++;
|
||||
}
|
||||
|
||||
b = (original_length & 0x00000000000000FF) >> 0;
|
||||
@<Process one byte of message@>;
|
||||
<<Process one byte of message>>;
|
||||
b = (original_length & 0x000000000000FF00) >> 8;
|
||||
@<Process one byte of message@>;
|
||||
<<Process one byte of message>>;
|
||||
b = (original_length & 0x0000000000FF0000) >> 16;
|
||||
@<Process one byte of message@>;
|
||||
<<Process one byte of message>>;
|
||||
b = (original_length & 0x00000000FF000000) >> 24;
|
||||
@<Process one byte of message@>;
|
||||
<<Process one byte of message>>;
|
||||
b = (original_length & 0x000000FF00000000) >> 32;
|
||||
@<Process one byte of message@>;
|
||||
<<Process one byte of message>>;
|
||||
b = (original_length & 0x0000FF0000000000) >> 40;
|
||||
@<Process one byte of message@>;
|
||||
<<Process one byte of message>>;
|
||||
b = (original_length & 0x00FF000000000000) >> 48;
|
||||
@<Process one byte of message@>;
|
||||
<<Process one byte of message>>;
|
||||
b = (original_length & 0xFF00000000000000) >> 56;
|
||||
@<Process one byte of message@>;
|
||||
<<Process one byte of message>>;
|
||||
|
||||
WRITE("%02x%02x%02x%02x",
|
||||
a0 % 0x100, (a0 >> 8) % 0x100, (a0 >> 16) % 0x100, (a0 >> 24) % 0x100);
|
||||
|
@ -337,7 +337,7 @@ void BinaryFiles::md5(OUTPUT_STREAM, filename *F, int (*mask)(uint64_t)) {
|
|||
BinaryFiles::close(bin);
|
||||
}
|
||||
|
||||
@<Process one byte of message@> =
|
||||
<<Process one byte of message>>=
|
||||
buffer[bc++] = (b % 0x100);
|
||||
if (bc == 64) {
|
||||
bc = 0;
|
||||
|
@ -377,9 +377,9 @@ void BinaryFiles::md5(OUTPUT_STREAM, filename *F, int (*mask)(uint64_t)) {
|
|||
}
|
||||
|
||||
@ This is a C-compiler safe way to rotate a 32-bit unsigned integer left
|
||||
by |shift| number of bits. Enjoy:
|
||||
by [[shift]] number of bits. Enjoy:
|
||||
|
||||
=
|
||||
<<*>>=
|
||||
uint32_t BinaryFiles::rotate(uint32_t value, uint32_t shift) {
|
||||
if ((shift &= sizeof(value)*8 - 1) == 0) return value;
|
||||
return (value << shift) | (value >> (sizeof(value)*8 - shift));
|
|
@ -3,45 +3,45 @@
|
|||
These utility routines look at the headers of JPEG and PNG files to find the
|
||||
pixel dimensions of any images supplied by the user for cover art and figures.
|
||||
|
||||
@h JPEG files.
|
||||
@ \section{JPEG files.}
|
||||
The following code, contributed by Toby Nelson, either finds the pixel width
|
||||
and height of a given JPEG file and returns |TRUE| or, if it can't read the
|
||||
file or doesn't recognise the header as having JPEG format, returns |FALSE|.
|
||||
and height of a given JPEG file and returns [[TRUE]] or, if it can't read the
|
||||
file or doesn't recognise the header as having JPEG format, returns [[FALSE]].
|
||||
|
||||
JPEG is properly speaking not a file format but a compression technique:
|
||||
the routine below works with either JIF (JPEG Interchange Format) or its
|
||||
simpler cousin JFIF (JPEG File Interchange Format).
|
||||
|
||||
We scan the file looking for "markers", each of which begins with an
|
||||
|0xFF| byte and is followed by a marker-type byte which is neither |0x00|
|
||||
nor |0xFF|. The compulsory marker SOI must appear at the start of the file,
|
||||
[[0xFF]] byte and is followed by a marker-type byte which is neither [[0x00]]
|
||||
nor [[0xFF]]. The compulsory marker SOI must appear at the start of the file,
|
||||
providing one way to detect probable JPEGs by looking at the first two
|
||||
bytes. There must also eventually be a start of frame marker, for the
|
||||
actual image: this can have many forms, but in all cases tells us the
|
||||
height and width.
|
||||
|
||||
=
|
||||
<<*>>=
|
||||
int ImageFiles::get_JPEG_dimensions(FILE *JPEG_file, unsigned int *width, unsigned int *height) {
|
||||
unsigned int sig, length;
|
||||
int marker;
|
||||
|
||||
if (!BinaryFiles::read_int16(JPEG_file, &sig)) return FALSE;
|
||||
if (sig != 0xFFD8) return FALSE; /* |0xFF| (marker) then |0xD8| (SOI) */
|
||||
if (sig != 0xFFD8) return FALSE; /* [[0xFF]] (marker) then [[0xD8]] (SOI) */
|
||||
|
||||
do {
|
||||
do {
|
||||
marker = getc(JPEG_file);
|
||||
if (marker == EOF) return FALSE;
|
||||
} while (marker != 0xff); /* skip to next |0xFF| byte */
|
||||
} while (marker != 0xff); /* skip to next [[0xFF]] byte */
|
||||
|
||||
do {
|
||||
marker = getc(JPEG_file);
|
||||
} while (marker == 0xff); /* skip to next non |FF| byte */
|
||||
} while (marker == 0xff); /* skip to next non [[FF]] byte */
|
||||
|
||||
if (!BinaryFiles::read_int16(JPEG_file, &length)) return FALSE; /* length of marker */
|
||||
|
||||
switch(marker) {
|
||||
/* all variant forms of "start of frame": e.g., |0xC0| is a baseline DCT image */
|
||||
/* all variant forms of "start of frame": e.g., [[0xC0]] is a baseline DCT image */
|
||||
case 0xc0:
|
||||
case 0xc1: case 0xc2: case 0xc3:
|
||||
case 0xc5: case 0xc6: case 0xc7:
|
||||
|
@ -65,7 +65,7 @@ int ImageFiles::get_JPEG_dimensions(FILE *JPEG_file, unsigned int *width, unsign
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
@h PNG files.
|
||||
@ \section{PNG files.}
|
||||
The PNG file must start with a signature which indicates that the
|
||||
remainder contains a single PNG image, consisting of a series of chunks
|
||||
beginning with an IHDR chunk and ending with an IEND chunk ("Portable
|
||||
|
@ -73,7 +73,7 @@ Network Graphics (PNG) Specification", 2nd edition, section 5.2). We only
|
|||
need to scan the IHDR chunk, of which the pixel width and height are the
|
||||
first two words (section 11.2.2).
|
||||
|
||||
=
|
||||
<<*>>=
|
||||
int ImageFiles::get_PNG_dimensions(FILE *PNG_file, unsigned int *width, unsigned int *height) {
|
||||
unsigned int sig1, sig2, length, type;
|
||||
|
|
@ -3,12 +3,12 @@
|
|||
These utility routines look at the headers of AIFF, OGG Vorbis or MIDI files
|
||||
to find the durations, and verify that they are what they purport to be.
|
||||
|
||||
@h AIFF files.
|
||||
@ \section{AIFF files.}
|
||||
The code in this section was once again originated by Toby Nelson. To
|
||||
explicate the following, see the specifications for AIFF and OGG headers.
|
||||
Durations are measured in centiseconds.
|
||||
|
||||
=
|
||||
<<*>>=
|
||||
int SoundFiles::get_AIFF_duration(FILE *pFile, unsigned int *pDuration,
|
||||
unsigned int *pBitsPerSecond, unsigned int *pChannels, unsigned int *pSampleRate) {
|
||||
unsigned int sig;
|
||||
|
@ -18,19 +18,19 @@ int SoundFiles::get_AIFF_duration(FILE *pFile, unsigned int *pDuration,
|
|||
unsigned int sampleSize;
|
||||
|
||||
if (!BinaryFiles::read_int32(pFile, &sig)) return FALSE;
|
||||
if (sig != 0x464F524D) return FALSE; /* |"FORM"| indicating an IFF file */
|
||||
if (sig != 0x464F524D) return FALSE; /* [["FORM"]] indicating an IFF file */
|
||||
|
||||
if (!BinaryFiles::read_int32(pFile, &sig)) return FALSE;
|
||||
|
||||
if (!BinaryFiles::read_int32(pFile, &sig)) return FALSE;
|
||||
if (sig != 0x41494646) return FALSE; /* |"AIFF"| indicating an AIFF file */
|
||||
if (sig != 0x41494646) return FALSE; /* [["AIFF"]] indicating an AIFF file */
|
||||
|
||||
/* Read chunks, skipping over those we are not interested in */
|
||||
while (TRUE) {
|
||||
if (!BinaryFiles::read_int32(pFile, &chunkID)) return FALSE;
|
||||
if (!BinaryFiles::read_int32(pFile, &chunkLength)) return FALSE;
|
||||
|
||||
if (chunkID == 0x434F4D4D) { /* |"COMM"| indicates common AIFF data */
|
||||
if (chunkID == 0x434F4D4D) { /* [["COMM"]] indicates common AIFF data */
|
||||
if (chunkLength < 18) return FALSE; /* Check we have enough data to read */
|
||||
|
||||
if (!BinaryFiles::read_int16(pFile, pChannels)) return FALSE;
|
||||
|
@ -53,9 +53,9 @@ int SoundFiles::get_AIFF_duration(FILE *pFile, unsigned int *pDuration,
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
@h OGG Vorbis files.
|
||||
@ \section{OGG Vorbis files.}
|
||||
|
||||
=
|
||||
<<*>>=
|
||||
int SoundFiles::get_OggVorbis_duration(FILE *pFile, unsigned int *pDuration,
|
||||
unsigned int *pBitsPerSecond, unsigned int *pChannels, unsigned int *pSampleRate) {
|
||||
unsigned int sig;
|
||||
|
@ -70,7 +70,7 @@ int SoundFiles::get_OggVorbis_duration(FILE *pFile, unsigned int *pDuration,
|
|||
unsigned char buffer[256];
|
||||
|
||||
if (!BinaryFiles::read_int32(pFile, &sig)) return FALSE;
|
||||
if (sig != 0x4F676753) return FALSE; /* |"OggS"| indicating an OGG file */
|
||||
if (sig != 0x4F676753) return FALSE; /* [["OggS"]] indicating an OGG file */
|
||||
|
||||
/* Check OGG version is zero */
|
||||
if (!BinaryFiles::read_int8(pFile, &version)) return FALSE;
|
||||
|
@ -90,10 +90,10 @@ int SoundFiles::get_OggVorbis_duration(FILE *pFile, unsigned int *pDuration,
|
|||
if (packetType != 1) return FALSE;
|
||||
|
||||
if (!BinaryFiles::read_int32(pFile, &vorbisSig1)) return FALSE;
|
||||
if (vorbisSig1 != 0x766F7262) return FALSE; /* |"VORB"| */
|
||||
if (vorbisSig1 != 0x766F7262) return FALSE; /* [["VORB"]] */
|
||||
|
||||
if (!BinaryFiles::read_int16(pFile, &vorbisSig2)) return FALSE;
|
||||
if (vorbisSig2 != 0x6973) return FALSE; /* |"IS"| */
|
||||
if (vorbisSig2 != 0x6973) return FALSE; /* [["IS"]] */
|
||||
|
||||
/* Check Vorbis version is zero */
|
||||
if (!BinaryFiles::read_int32(pFile, &version)) return FALSE;
|
||||
|
@ -117,7 +117,7 @@ int SoundFiles::get_OggVorbis_duration(FILE *pFile, unsigned int *pDuration,
|
|||
if (pBitsPerSecond == 0) return FALSE;
|
||||
|
||||
/* Search for the final Ogg page (near the end of the file) to read duration, */
|
||||
/* i.e., read the last 4K of the file and look for the final |"OggS"| sig */
|
||||
/* i.e., read the last 4K of the file and look for the final [["OggS"]] sig */
|
||||
if (fseek(pFile, 0, SEEK_END) != 0) return FALSE;
|
||||
fileLength = (unsigned int) ftell(pFile);
|
||||
if (fileLength < 4096) seekPos = 0;
|
||||
|
@ -148,7 +148,7 @@ int SoundFiles::get_OggVorbis_duration(FILE *pFile, unsigned int *pDuration,
|
|||
|
||||
if (fseek(pFile, (long) lastSig, SEEK_SET) != 0) return FALSE;
|
||||
if (!BinaryFiles::read_int32(pFile, &sig)) return FALSE;
|
||||
if (sig != 0x4F676753) return FALSE; /* |"OggS"| indicating an OGG file */
|
||||
if (sig != 0x4F676753) return FALSE; /* [["OggS"]] indicating an OGG file */
|
||||
|
||||
/* Check OGG version is zero */
|
||||
if (!BinaryFiles::read_int8(pFile, &version)) return FALSE;
|
||||
|
@ -166,7 +166,7 @@ int SoundFiles::get_OggVorbis_duration(FILE *pFile, unsigned int *pDuration,
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
@h MIDI files.
|
||||
@ \section{MIDI files.}
|
||||
At one time it was proposed that Inform 7 should allow a third sound file
|
||||
format: MIDI. This provoked considerable debate in July 2007 and enough
|
||||
doubts were raised that the implementation below was never in fact
|
||||
|
@ -177,7 +177,7 @@ it can only usefully provide sound files which the virtual machines it
|
|||
compiles for will allow. At present, the Glulx virtual machine does not
|
||||
officially support MIDI, which makes the question moot.
|
||||
|
||||
=
|
||||
<<*>>=
|
||||
int SoundFiles::get_MIDI_information(FILE *pFile, unsigned int *pType,
|
||||
unsigned int *pNumTracks) {
|
||||
unsigned int sig;
|
||||
|
@ -196,7 +196,7 @@ int SoundFiles::get_MIDI_information(FILE *pFile, unsigned int *pType,
|
|||
|
||||
if (!BinaryFiles::read_int32(pFile, &sig)) return FALSE;
|
||||
|
||||
/* |"RIFF"| indicating a RIFF file */
|
||||
/* [["RIFF"]] indicating a RIFF file */
|
||||
if (sig == 0x52494646) {
|
||||
/* Skip the filesize and typeID */
|
||||
if (fseek(pFile, 8, SEEK_CUR) != 0) return FALSE;
|
||||
|
@ -205,7 +205,7 @@ int SoundFiles::get_MIDI_information(FILE *pFile, unsigned int *pType,
|
|||
if (!BinaryFiles::read_int32(pFile, &sig)) return FALSE;
|
||||
}
|
||||
|
||||
/* |"MThd"| indicating a MIDI file */
|
||||
/* [["MThd"]] indicating a MIDI file */
|
||||
if (sig != 0x4D546864) return FALSE;
|
||||
|
||||
/* Read length of chunk */
|
||||
|
@ -250,7 +250,7 @@ int SoundFiles::get_MIDI_information(FILE *pFile, unsigned int *pType,
|
|||
/* Skip any remaining bytes in the MThd chunk */
|
||||
if (fseek(pFile, (long) (length - 6), SEEK_CUR) != 0) return FALSE;
|
||||
|
||||
/* Keep reading chunks, looking for |"MTrk"| */
|
||||
/* Keep reading chunks, looking for [["MTrk"]] */
|
||||
do {
|
||||
/* Read chunk signature and length */
|
||||
if (!BinaryFiles::read_int32(pFile, &sig)) {
|
||||
|
@ -261,7 +261,7 @@ int SoundFiles::get_MIDI_information(FILE *pFile, unsigned int *pType,
|
|||
|
||||
start_of_chunk_data = (unsigned int) ftell(pFile);
|
||||
|
||||
if (sig == 0x4D54726B) { /* |"MTrk"| */
|
||||
if (sig == 0x4D54726B) { /* [["MTrk"]] */
|
||||
LOG("track starts\n");
|
||||
/* Read each event, looking for information before the real tune starts, e.g., tempo */
|
||||
do {
|
Loading…
Reference in a new issue