Task 403: .MNT File Format
Task 403: .MNT File Format
The .MNT file format is the Visual FoxPro Menu Memo format, a binary format used to store memo (variable-length) data associated with menu structures in Visual FoxPro applications. It is structurally identical to the FoxPro .FPT memo file format.
- The properties of this file format intrinsic to its file system are:
- Next Free Block Number (4 bytes, big-endian unsigned integer at offset 0)
- Unused (2 bytes at offset 4)
- Block Size (2 bytes, big-endian unsigned integer at offset 6)
- Reserved Area (504 bytes at offset 8)
- Memo Blocks (variable number, starting at offset 512, each block is of fixed size specified by the block size property, and consists of:
- Type (4 bytes, big-endian unsigned integer; typically 0 for picture/binary or 1 for text/memo)
- Length (4 bytes, big-endian unsigned integer; the size of the data payload)
- Data (variable bytes equal to the length value; the actual memo content)
- Padding (remaining bytes to fill the full block size; usually zeros or garbage)
I was unable to find direct download links for .MNT files after searching available sources. No valid examples were located.
Here is the HTML with embedded JavaScript for a simple page that allows drag-and-drop of a .MNT file and dumps the properties to the screen:
Drag and drop a .MNT file here
- Here is the Python class:
import struct
class MNTFile:
def __init__(self, filename):
self.filename = filename
self.next_free_block = 0
self.unused = 0
self.block_size = 0
self.reserved = b''
self.memo_blocks = [] # list of dicts: {'type': int, 'length': int, 'data': bytes}
def read(self):
with open(self.filename, 'rb') as f:
data = f.read()
if len(data) < 512:
raise ValueError("Invalid .MNT file")
self.next_free_block = struct.unpack('>I', data[0:4])[0]
self.unused = struct.unpack('>H', data[4:6])[0]
self.block_size = struct.unpack('>H', data[6:8])[0]
self.reserved = data[8:512]
offset = 512
block_number = 1
while offset + self.block_size <= len(data):
type_ = struct.unpack('>I', data[offset:offset+4])[0]
length = struct.unpack('>I', data[offset+4:offset+8])[0]
if length > 0 and (type_ == 0 or type_ == 1):
end = offset + 8 + length
if end > len(data):
break
memo_data = data[offset+8:end]
self.memo_blocks.append({'type': type_, 'length': length, 'data': memo_data})
offset += self.block_size
block_number += 1
def print_properties(self):
print("Next Free Block Number:", self.next_free_block)
print("Unused:", self.unused)
print("Block Size:", self.block_size)
print("Reserved Area: (504 bytes, hex):", self.reserved.hex()[:100] + "...") # Truncated for brevity
for idx, block in enumerate(self.memo_blocks, 1):
print(f"Memo Block {idx}:")
print(" Type:", block['type'])
print(" Length:", block['length'])
print(" Data (first 100 bytes as string):", block['data'][:100].decode('utf-8', errors='ignore') + "...")
def write(self, new_filename=None):
if new_filename is None:
new_filename = self.filename
header = struct.pack('>I', self.next_free_block) + struct.pack('>H', self.unused) + struct.pack('>H', self.block_size) + self.reserved
body = b''
for block in self.memo_blocks:
block_header = struct.pack('>I', block['type']) + struct.pack('>I', block['length'])
padding_size = self.block_size - (8 + block['length'])
body += block_header + block['data'] + b'\x00' * padding_size
with open(new_filename, 'wb') as f:
f.write(header + b'\x00' * (512 - len(header)) + body) # Ensure header is 512 bytes
# Example usage:
# mnt = MNTFile('example.mnt')
# mnt.read()
# mnt.print_properties()
# mnt.write('modified.mnt')
- Here is the Java class:
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
public class MNTFile {
private String filename;
private int nextFreeBlock;
private short unused;
private short blockSize;
private byte[] reserved = new byte[504];
private java.util.List<MemoBlock> memoBlocks = new java.util.ArrayList<>();
public MNTFile(String filename) {
this.filename = filename;
}
public void read() throws IOException {
try (FileInputStream fis = new FileInputStream(filename)) {
byte[] data = fis.readAllBytes();
if (data.length < 512) {
throw new IOException("Invalid .MNT file");
}
ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.BIG_ENDIAN);
nextFreeBlock = bb.getInt(0);
unused = bb.getShort(4);
blockSize = bb.getShort(6);
bb.position(8);
bb.get(reserved);
int offset = 512;
int blockNumber = 1;
while (offset + blockSize <= data.length) {
int type = bb.getInt(offset);
int length = bb.getInt(offset + 4);
if (length > 0 && (type == 0 || type == 1)) {
byte[] memoData = new byte[length];
bb.position(offset + 8);
bb.get(memoData);
memoBlocks.add(new MemoBlock(type, length, memoData));
}
offset += blockSize;
blockNumber++;
}
}
}
public void printProperties() {
System.out.println("Next Free Block Number: " + nextFreeBlock);
System.out.println("Unused: " + unused);
System.out.println("Block Size: " + blockSize);
System.out.print("Reserved Area (first 100 bytes hex): ");
for (int i = 0; i < Math.min(100, reserved.length); i++) {
System.out.print(String.format("%02X ", reserved[i]));
}
System.out.println("...");
for (int i = 0; i < memoBlocks.size(); i++) {
MemoBlock block = memoBlocks.get(i);
System.out.println("Memo Block " + (i + 1) + ":");
System.out.println(" Type: " + block.type);
System.out.println(" Length: " + block.length);
System.out.print(" Data (first 100 bytes as string): ");
System.out.println(new String(block.data, 0, Math.min(100, block.length)));
}
}
public void write(String newFilename) throws IOException {
if (newFilename == null) {
newFilename = filename;
}
ByteBuffer bb = ByteBuffer.allocate(512).order(ByteOrder.BIG_ENDIAN);
bb.putInt(nextFreeBlock);
bb.putShort(unused);
bb.putShort(blockSize);
bb.put(reserved);
byte[] header = bb.array(); // 512 bytes
try (FileOutputStream fos = new FileOutputStream(newFilename)) {
fos.write(header);
for (MemoBlock block : memoBlocks) {
ByteBuffer blockBb = ByteBuffer.allocate(blockSize).order(ByteOrder.BIG_ENDIAN);
blockBb.putInt(block.type);
blockBb.putInt(block.length);
blockBb.put(block.data);
// Padding added automatically as zeros
fos.write(blockBb.array());
}
}
}
private static class MemoBlock {
int type;
int length;
byte[] data;
MemoBlock(int type, int length, byte[] data) {
this.type = type;
this.length = length;
this.data = data;
}
}
// Example usage:
// MNTFile mnt = new MNTFile("example.mnt");
// mnt.read();
// mnt.printProperties();
// mnt.write("modified.mnt");
}
- Here is the JavaScript class (for Node.js, using fs module for file I/O):
const fs = require('fs');
class MNTFile {
constructor(filename) {
this.filename = filename;
this.nextFreeBlock = 0;
this.unused = 0;
this.blockSize = 0;
this.reserved = Buffer.alloc(504);
this.memoBlocks = []; // array of {type: number, length: number, data: Buffer}
}
read() {
const data = fs.readFileSync(this.filename);
if (data.length < 512) {
throw new Error('Invalid .MNT file');
}
this.nextFreeBlock = data.readUInt32BE(0);
this.unused = data.readUInt16BE(4);
this.blockSize = data.readUInt16BE(6);
data.copy(this.reserved, 0, 8, 512);
let offset = 512;
let blockNumber = 1;
while (offset + this.blockSize <= data.length) {
const type = data.readUInt32BE(offset);
const length = data.readUInt32BE(offset + 4);
if (length > 0 && (type === 0 || type === 1)) {
const memoData = Buffer.alloc(length);
data.copy(memoData, 0, offset + 8, offset + 8 + length);
this.memoBlocks.push({type, length, data: memoData});
}
offset += this.blockSize;
blockNumber++;
}
}
printProperties() {
console.log('Next Free Block Number:', this.nextFreeBlock);
console.log('Unused:', this.unused);
console.log('Block Size:', this.blockSize);
console.log('Reserved Area (first 100 bytes hex):', this.reserved.slice(0, 100).toString('hex'));
this.memoBlocks.forEach((block, index) => {
console.log(`Memo Block ${index + 1}:`);
console.log(' Type:', block.type);
console.log(' Length:', block.length);
console.log(' Data (first 100 bytes as string):', block.data.slice(0, 100).toString('utf8'));
});
}
write(newFilename = this.filename) {
let buffer = Buffer.alloc(512);
buffer.writeUInt32BE(this.nextFreeBlock, 0);
buffer.writeUInt16BE(this.unused, 4);
buffer.writeUInt16BE(this.blockSize, 6);
this.reserved.copy(buffer, 8);
this.memoBlocks.forEach(block => {
let blockBuffer = Buffer.alloc(this.blockSize);
blockBuffer.writeUInt32BE(block.type, 0);
blockBuffer.writeUInt32BE(block.length, 4);
block.data.copy(blockBuffer, 8);
// Padding is zeros by default
buffer = Buffer.concat([buffer, blockBuffer]);
});
fs.writeFileSync(newFilename, buffer);
}
}
// Example usage:
// const mnt = new MNTFile('example.mnt');
// mnt.read();
// mnt.printProperties();
// mnt.write('modified.mnt');
- Here is the C "class" (using struct with functions, as C does not have classes):
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <endian.h> // For big-endian conversions, assume available or use custom
typedef struct {
uint32_t type;
uint32_t length;
uint8_t *data;
} MemoBlock;
typedef struct {
char *filename;
uint32_t next_free_block;
uint16_t unused;
uint16_t block_size;
uint8_t reserved[504];
MemoBlock *memo_blocks;
size_t num_memo_blocks;
} MNTFile;
MNTFile *mnt_create(const char *filename) {
MNTFile *mnt = malloc(sizeof(MNTFile));
mnt->filename = strdup(filename);
mnt->next_free_block = 0;
mnt->unused = 0;
mnt->block_size = 0;
memset(mnt->reserved, 0, 504);
mnt->memo_blocks = NULL;
mnt->num_memo_blocks = 0;
return mnt;
}
void mnt_destroy(MNTFile *mnt) {
for (size_t i = 0; i < mnt->num_memo_blocks; i++) {
free(mnt->memo_blocks[i].data);
}
free(mnt->memo_blocks);
free(mnt->filename);
free(mnt);
}
int mnt_read(MNTFile *mnt) {
FILE *f = fopen(mnt->filename, "rb");
if (!f) return 1;
fseek(f, 0, SEEK_END);
long file_size = ftell(f);
fseek(f, 0, SEEK_SET);
uint8_t *data = malloc(file_size);
fread(data, 1, file_size, f);
fclose(f);
if (file_size < 512) {
free(data);
return 1;
}
mnt->next_free_block = be32toh(*(uint32_t*)(data + 0));
mnt->unused = be16toh(*(uint16_t*)(data + 4));
mnt->block_size = be16toh(*(uint16_t*)(data + 6));
memcpy(mnt->reserved, data + 8, 504);
long offset = 512;
size_t block_number = 1;
while (offset + mnt->block_size <= file_size) {
uint32_t type = be32toh(*(uint32_t*)(data + offset));
uint32_t length = be32toh(*(uint32_t*)(data + offset + 4));
if (length > 0 && (type == 0 || type == 1)) {
uint8_t *memo_data = malloc(length);
memcpy(memo_data, data + offset + 8, length);
mnt->memo_blocks = realloc(mnt->memo_blocks, sizeof(MemoBlock) * (mnt->num_memo_blocks + 1));
mnt->memo_blocks[mnt->num_memo_blocks].type = type;
mnt->memo_blocks[mnt->num_memo_blocks].length = length;
mnt->memo_blocks[mnt->num_memo_blocks].data = memo_data;
mnt->num_memo_blocks++;
}
offset += mnt->block_size;
block_number++;
}
free(data);
return 0;
}
void mnt_print_properties(const MNTFile *mnt) {
printf("Next Free Block Number: %u\n", mnt->next_free_block);
printf("Unused: %hu\n", mnt->unused);
printf("Block Size: %hu\n", mnt->block_size);
printf("Reserved Area (first 100 bytes hex): ");
for (int i = 0; i < 100 && i < 504; i++) {
printf("%02x ", mnt->reserved[i]);
}
printf("...\n");
for (size_t i = 0; i < mnt->num_memo_blocks; i++) {
MemoBlock block = mnt->memo_blocks[i];
printf("Memo Block %zu:\n", i + 1);
printf(" Type: %u\n", block.type);
printf(" Length: %u\n", block.length);
printf(" Data (first 100 bytes as string): ");
for (int j = 0; j < 100 && j < block.length; j++) {
putchar(block.data[j]);
}
printf("...\n");
}
}
int mnt_write(const MNTFile *mnt, const char *new_filename) {
if (!new_filename) new_filename = mnt->filename;
FILE *f = fopen(new_filename, "wb");
if (!f) return 1;
uint8_t header[512] = {0};
*(uint32_t*)(header + 0) = htobe32(mnt->next_free_block);
*(uint16_t*)(header + 4) = htobe16(mnt->unused);
*(uint16_t*)(header + 6) = htobe16(mnt->block_size);
memcpy(header + 8, mnt->reserved, 504);
fwrite(header, 1, 512, f);
for (size_t i = 0; i < mnt->num_memo_blocks; i++) {
MemoBlock block = mnt->memo_blocks[i];
uint8_t *block_buf = calloc(1, mnt->block_size);
*(uint32_t*)(block_buf + 0) = htobe32(block.type);
*(uint32_t*)(block_buf + 4) = htobe32(block.length);
memcpy(block_buf + 8, block.data, block.length);
fwrite(block_buf, 1, mnt->block_size, f);
free(block_buf);
}
fclose(f);
return 0;
}
// Example usage:
// MNTFile *mnt = mnt_create("example.mnt");
// mnt_read(mnt);
// mnt_print_properties(mnt);
// mnt_write(mnt, "modified.mnt");
// mnt_destroy(mnt);