foundation-module: Chapter 6: Nowebify.

This commit is contained in:
AwesomeAdam54321 2024-03-09 23:24:15 +08:00
parent 82de01cd6f
commit 4eee2a66d4
3 changed files with 64 additions and 64 deletions

View file

@ -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));

View file

@ -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;

View file

@ -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 {