Displaying raw pixel array on a web page using image tag - html

I am trying to figure out the best way to put a raw pixel array into an image tag. The pixels are served from a server that does not have a png or jpg compression library so the raw array comes in via an HTTP request. I can control the return headers so I put a mime type in the response. I'd like to do:
<img src="http://myserver.com/id/" />
But I don't think I can do that. I could use the src="data:XXXXXX;base64,http://myserver.com/id/" if that works, but I need to know what to do with XXXX.
Another idea I've had is using svg if I can set an image equal to SVG. Not sure if i'd have to wrap each pixel in an element.
Maybe there is a way to do this with CSS?
I can write the data to canvas element with js pretty easily, but I was hoping to have a non-js way.
I can do some minor manipulation of the binary structure of the data coming out of the server, so if there is an easy way to tell the jpg or png format that this is uncompressed data, I could do that...I just don't have the horsepower or the time to translate to the png or jpg libraries to the (blockchain) based language I'm having to use.

Relevant links: the zlib RFC, the DEFLATE RFC, and the PNG spec. You may wish to consult these while reading.
Let's make a PNG!
Start with the image data as an RGBA image, for example for a 2x2 image:
12 34 56 78 9a bc de f0
cd ef 01 23 45 67 89 ab
Specify filters
For each row of the image, add a null byte to the beginning to set the PNG filter to none. For example,
00 12 34 56 78 9a bc de f0 # row 1 of image
00 cd ef 01 23 45 67 89 ab # row 2 of image
DEFLATE encode it
While this is a compression format, we don't actually need to compress it! DEFLATE provides a way to encode data without compressing it (see section 3.2.4 of the DEFLATE spec). Find the length of the filtered image data from above as a 16-bit integer (if it is bigger than 65535, then split the image data into 65535-sized chunks, and do this step for each chunk). Create an empty array to hold the DEFLATE data stream. Insert that size as a 16-bit integer in big-endian format into the currently empty DEFLATE data stream. Next, append the bitwise inverse of the length. Finally, insert the filtered image data into the data stream.
Wrap it in zlib
Next, we need to zlib-encode the data. Start with an empty zlib data stream, and insert 2 null bytes for the header. Next, insert the DEFLATEd data from above. Finally, insert the Adler32 checksum of the uncompressed data. Specifically, the Adler32 checksum should be created from the filtered image data.
Wrap it in a PNG format
Finally, let's wrap this into a PNG. Replace the width and height values, specify the length of the IDAT chunk as the length of the zlib-encoded data, and replace the CRCs of the IDHR and IDAT chunks by taking the CRC32 checksum of the the chunk name concatenated with the data.
# PNG signature
137 80 78 71 13 10 26 10
# IDHR chunk
00 00 00 0d # IDHR length
73 72 68 82 # IDHR type
00 00 00 02 # width: COMPUTE THIS
00 00 00 02 # height: COMPUTE THIS
08 # bit depth: 8
06 # colour type: truecolour with alpha
00 # compression method: zlib
00 # filter method: normal
00 # no interlacing
55 55 55 55 # CRC: COMPUTE THIS from "IDHR" + data
# IDAT chunk
55 55 55 55 # IDAT length: COMPUTE THIS
73 68 65 84 # IDAT type
[zlib data]
55 55 55 55 # CRC: COMPUTE THIS from the "IDAT" + data
# IEND chunk
00 00 00 00 # IEND length
73 69 78 68 # IEND type
AE 42 60 82 # CRC (always the same value, doesn't need to be computed)

Austin, I see from your previous comment that the pixel data you are receiving comes in RGBA format with four bytes per pixel (e.g. R, G, B, A).
** If you can provide a sample of the data returned, I can refactor my example to use the RGBA values returned directly. It should actually be easier, as I believe I can plug them directly into the rgba(r, g, b, a) CSS function without having to convert them to hex.
I use this exact same process in my console.draw() tool, and it works flawlessly for me, converting raw pixel data into valid <img> tags on demand.
If you receive the pixels as an array of colors, you will either have to supply the function you use with the number of pixels per row so it knows where to wrap to the next lines, or more appropriately, use an array of nested arrays, one nested array per row of pixels. Then, draw the array of nested pixel colors to a canvas, row by row, pixel by pixel. Finally, use the HTMLCanvasElement.toDataURL() method to convert the canvas image to a Base64 encoded string which you can assign to the src attribute value of the img tag.
If you would like to display the final image pixelated, without any anti-aliasing, make sure to apply the CSS rule image-rendering: pixelated.
Here is all of this, put into action:
const pixels2Base64 = pixelColors => {
const canvas = document.createElement('canvas');
canvas.width = pixelColors[0].length;
canvas.height = pixelColors.length;
const context = canvas.getContext('2d');
for (let i = 0; i < pixelColors.length; i++) {
for (let j = 0; j < pixelColors[i].length; j++) {
context.fillStyle = pixelColors[i][j];
context.fillRect(j, i, 1, 1);
}
}
const dataURL = canvas.toDataURL('image/png', 1);
canvas.remove();
return dataURL;
};
const nyanCat = [["#00000000","#00000000","#00000000","#ff1111ff","#ff1111ff","#ff1111ff","#00000000","#00000000","#00000000","#00000000","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#00000000","#00000000","#00000000","#00000000","#00000000","#00000000","#00000000","#00000000"],["#ff1111ff","#ff1111ff","#ff1111ff","#ff1111ff","#ff1111ff","#ff1111ff","#ff1111ff","#ff1111ff","#ff1111ff","#000000ff","#ffd29bff","#ffd29bff","#ffd29bff","#ffd29bff","#ffd29bff","#ffd29bff","#ffd29bff","#ffd29bff","#ffd29bff","#ffd29bff","#ffd29bff","#ffd29bff","#ffd29bff","#ffd29bff","#ffd29bff","#ffd29bff","#ffd29bff","#ffd29bff","#000000ff","#00000000","#00000000","#00000000","#00000000","#00000000","#00000000","#00000000"],["#ff1111ff","#ff1111ff","#ff1111ff","#ff1111ff","#ff1111ff","#ff1111ff","#ff1111ff","#ff1111ff","#000000ff","#ffd29bff","#ffd29bff","#ffd29bff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#ffd29bff","#ffd29bff","#ffd29bff","#000000ff","#00000000","#00000000","#00000000","#00000000","#00000000","#00000000"],["#ff1111ff","#ff1111ff","#ff1111ff","#fea70aff","#fea70aff","#fea70aff","#ff1111ff","#ff1111ff","#000000ff","#ffd29bff","#ffd29bff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#eb4ab4ff","#fea4feff","#fea4feff","#eb4ab4ff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#ffd29bff","#ffd29bff","#000000ff","#00000000","#00000000","#00000000","#00000000","#00000000","#00000000"],["#fea70aff","#fea70aff","#fea70aff","#fea70aff","#fea70aff","#fea70aff","#fea70aff","#fea70aff","#000000ff","#ffd29bff","#fea4feff","#fea4feff","#eb4ab4ff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#ffd29bff","#000000ff","#00000000","#00000000","#00000000","#00000000","#00000000","#00000000"],["#fea70aff","#fea70aff","#fea70aff","#fea70aff","#fea70aff","#fea70aff","#fea70aff","#fea70aff","#000000ff","#ffd29bff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#000000ff","#000000ff","#fea4feff","#eb4ab4ff","#fea4feff","#fea4feff","#ffd29bff","#000000ff","#00000000","#00000000","#000000ff","#000000ff","#00000000","#00000000"],["#fea70aff","#fea70aff","#fea70aff","#fefe06ff","#fefe06ff","#fefe06ff","#fea70aff","#fea70aff","#000000ff","#ffd29bff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#000000ff","#a9a7aaff","#a9a7aaff","#000000ff","#fea4feff","#fea4feff","#fea4feff","#ffd29bff","#000000ff","#00000000","#000000ff","#a9a7aaff","#a9a7aaff","#000000ff","#00000000"],["#fefe06ff","#fefe06ff","#fefe06ff","#fefe06ff","#fefe06ff","#fefe06ff","#fefe06ff","#fefe06ff","#000000ff","#ffd29bff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#eb4ab4ff","#fea4feff","#fea4feff","#fea4feff","#000000ff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#000000ff","#fea4feff","#fea4feff","#ffd29bff","#000000ff","#000000ff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#000000ff","#00000000"],["#fefe06ff","#fefe06ff","#fefe06ff","#fefe06ff","#fefe06ff","#fefe06ff","#fefe06ff","#fefe06ff","#000000ff","#ffd29bff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#000000ff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#000000ff","#000000ff","#000000ff","#000000ff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#000000ff","#00000000"],["#fefe06ff","#fefe06ff","#fefe06ff","#48fe0bff","#48fe0bff","#48fe0bff","#fefe06ff","#fefe06ff","#000000ff","#ffd29bff","#fea4feff","#fea4feff","#fea4feff","#eb4ab4ff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#000000ff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#000000ff","#00000000"],["#48fe0bff","#48fe0bff","#48fe0bff","#48fe0bff","#48fe0bff","#48fe0bff","#48fe0bff","#000000ff","#000000ff","#ffd29bff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#eb4ab4ff","#fea4feff","#000000ff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#000000ff"],["#48fe0bff","#48fe0bff","#48fe0bff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#ffd29bff","#fea4feff","#eb4ab4ff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#000000ff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#fefefeff","#000000ff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#fefefeff","#000000ff","#a9a7aaff","#a9a7aaff","#000000ff"],["#48fe0bff","#000000ff","#000000ff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#000000ff","#ffd29bff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#000000ff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#000000ff","#000000ff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#000000ff","#a9a7aaff","#000000ff","#000000ff","#a9a7aaff","#a9a7aaff","#000000ff"],["#0eadfeff","#000000ff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#000000ff","#000000ff","#000000ff","#ffd29bff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#eb4ab4ff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#000000ff","#a9a7aaff","#fea4a6ff","#fea4a6ff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#fea4a6ff","#fea4a6ff","#000000ff"],["#0eadfeff","#0eadfeff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#0eadfeff","#000000ff","#ffd29bff","#fea4feff","#fea4feff","#eb4ab4ff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#000000ff","#a9a7aaff","#fea4a6ff","#fea4a6ff","#a9a7aaff","#000000ff","#a9a7aaff","#a9a7aaff","#000000ff","#a9a7aaff","#a9a7aaff","#000000ff","#a9a7aaff","#fea4a6ff","#fea4a6ff","#000000ff"],["#0eadfeff","#0eadfeff","#0eadfeff","#7543feff","#7543feff","#7543feff","#0eadfeff","#0eadfeff","#000000ff","#ffd29bff","#ffd29bff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#000000ff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#a9a7aaff","#a9a7aaff","#000000ff","#00000000"],["#7543feff","#7543feff","#7543feff","#7543feff","#7543feff","#7543feff","#7543feff","#7543feff","#000000ff","#000000ff","#ffd29bff","#ffd29bff","#ffd29bff","#ffd29bff","#ffd29bff","#ffd29bff","#ffd29bff","#ffd29bff","#ffd29bff","#ffd29bff","#ffd29bff","#ffd29bff","#000000ff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#000000ff","#00000000","#00000000"],["#7543feff","#7543feff","#7543feff","#7543feff","#7543feff","#7543feff","#7543feff","#7543feff","#000000ff","#a9a7aaff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#00000000","#00000000","#00000000"],["#7543feff","#7543feff","#7543feff","#00000000","#00000000","#00000000","#7543feff","#7543feff","#000000ff","#a9a7aaff","#a9a7aaff","#000000ff","#00000000","#000000ff","#a9a7aaff","#a9a7aaff","#000000ff","#00000000","#00000000","#00000000","#00000000","#00000000","#00000000","#00000000","#000000ff","#a9a7aaff","#a9a7aaff","#000000ff","#00000000","#000000ff","#a9a7aaff","#a9a7aaff","#000000ff","#00000000","#00000000","#00000000"],["#00000000","#00000000","#00000000","#00000000","#00000000","#00000000","#00000000","#00000000","#000000ff","#000000ff","#000000ff","#00000000","#00000000","#00000000","#000000ff","#000000ff","#000000ff","#00000000","#00000000","#00000000","#00000000","#00000000","#00000000","#00000000","#00000000","#000000ff","#000000ff","#000000ff","#00000000","#00000000","#000000ff","#000000ff","#000000ff","#00000000","#00000000","#00000000"]];
const img = document.querySelector('img');
img.src = pixels2Base64(nyanCat);
img {
width: 250px;
height: auto;
image-rendering: pixelated;
}
<img>

Related

Figuring out how a number is represented in hex form

Currently trying to essentially reverse engineer a file format that is produced by a CNC machine when backing up programs on the machine so that i can read the programs on a standard PC. Have opened a few of the backup files created and can clearly see patterns of data such as the program name etc. which can be clearly seen in plaintext form. One thing i am struggling with is how numbers are represented in this.
for Example: the number '20' is represented in this file in hex form as '40 0D 03 00'.
More examples:
"-213.6287": "21 67 DF FF"
"-500.3366": "9A A7 B3 FF"
Any help with trying to figure out how these hex values make up those numbers?
Thanks
These numbers are stored as little-endian signed integers, as a count of ten-thousandths.
for Example: the number '20' is represented in this file in hex form as '40 0D 03 00'.
0x00030d40 = 200000.
"-213.6287": "21 67 DF FF"
0xffdf6721 = -2136287.
"-500.3366": "9A A7 B3 FF"
0xffb3a79a = -5003366.

Paradox DB file signature

I'm searching in web for magic numbers for Paradox DB files (.db), but there are no results.
What binary signature for these files?
I hate to answer this question because identifying the type of a file by its contents is inherently unreliable -- a binary signature tells you only that the file MAY be of a certain type, but there's no guarantee that another file won't happen to contain that string.
Also, binary signature recognition of *.DB files is weak because almost all the bytes carry highly variable information, and most of the constant bytes are zero:
byte constant
offset value
-- --
2 00
3 08
E 00
F 01
14 00
2A 00
2B 00
2C 00
(all values in this answer are given in hexidecimal)
There's probably lots of non *.DB files that will accidentally match this pattern.
A couple more bytes are easily interpreted and provide additional information:
byte offset 4:
00 = indexed .DB data file
01 = primary index .PX file
02 = non-indexed .DB data file
03 = non-incrementing secondary index .Xnn file
04 = secondary index .Ynn file (inc or non-inc)
05 = incrementing secondary index .Xnn file
06 = non-incrementing secondary index .XGn file
07 = secondary index .YGn file (inc or non inc)
08 = incrementing secondary index .XGn file
byte offset 39:
03 version 3.0
04 version 3.5
05..09 version 4.x
0A..0B version 5.x
0C version 7.x
Borland never published the internal format of its data base files. I think that to reliably identify a *.DB file by its contents you would have to try to open it with Borland's data base engine.
-Al.

LZW Decompress: Why is first dictionary code encountered in TIFF strip 261 instead of 257, or am I misreading it?

I have a trivial RGB file saved as TIFF in Photoshop, 1000 or so pixels wide. The first row consists of 3 pixels all of which are hex 4B red, B0 green, 78 blue, and the rest of the row white.
The strip is LZW-encoded and the initial bytes of the strip are:
80 12 D6 07 80 04 16 0C B4 27 A1 E0 D0 B8 64 36 ... (actually only the first 7 or so bytes are significant to my question.)
In 9-bit segments this is:
100000000 001001011 010110000 001111000 000000000 100000101 100000110 ...
(0x100) (0x4B) (0xB0) (0x78) (0x00) (0x105) (0x106)
From what I understand 256 (0x100) is a reset code, but why is the first extended code after that 261 (0x105) instead of 257? I would expect whatever dictionary entry this points to to be the 4B/B0 pair for the second pixel (which it may well be), but how would the decompression algorithm know to place 4B/B0 at 261 instead of 257? Can someone explain what I'm missing here? Might there be something elsewhere in the .tif file that would indicate this? Thanks very much.
~
Let's see
256 (100h) is Clear
257 (101h) is EOF
in your case, then
4Bh B0h is 258 (102h)
B0h 78h is 259 (103h)
78h 00h is 260 (104h)
00h 00h is 261 (105h)
Looks good to me. LZW can actually encode one character ahead of what's been added to the table.

Interpret PNG pixel data

Looking at the PNG specification, it appears that the PNG pixel data chunk starts with IDAT and ends with IEND (slightly clearer explanation here). In the middle are values that don't make sense to make sense to me.
How can I get usable RGB values from this, without using any libraries (ie from the raw binary file)?
As an example, I made a 2x2px image with 4 black rgb(0,0,0) pixels in Photoshop:
Here's the resulting data (in the raw binary input, the hex values, and the human-readable ASCII):
BINARY HEX ASCII
01001001 49 'I'
01000100 44 'D'
01000001 41 'A'
01010100 54 'T'
01111000 78 'x'
11011010 DA '\xda'
01100010 62 'b'
01100000 60 '`'
01000000 40 '#'
00000110 06 '\x06'
00000000 00 '\x00'
00000000 00 '\x00'
00000000 00 '\x00'
00000000 00 '\x00'
11111111 FF '\xff'
11111111 FF '\xff'
00000011 03 '\x03'
00000000 00 '\x00'
00000000 00 '\x00'
00001110 0E '\x0e'
00000000 00 '\x00'
00000001 01 '\x01'
10000011 83 '\x83'
11010100 D4 '\xd4'
11101100 EC '\xec'
10001110 8E '\x8e'
00000000 00 '\x00'
00000000 00 '\x00'
00000000 00 '\x00'
00000000 00 '\x00'
01001001 49 'I'
01000101 45 'E'
01001110 4E 'N'
01000100 44 'D'
You missed a rather crucial detail in both the specifications:
The official one:
.. The IDAT chunk contains the actual image data which is the output stream of the compression algorithm.
[...]
Deflate-compressed datastreams within PNG are stored in the "zlib" format.
Wikipedia:
IDAT contains the image, which may be split among multiple IDAT chunks. Such splitting increases filesize slightly, but makes it possible to generate a PNG in a streaming manner. The IDAT chunk contains the actual image data, which is the output stream of the compression algorithm.
Both state the raw image data is compressed. Looking at your data, the first 2 bytes
78 DA
contain the compression flags as specified in RFC1950. The rest of the data is compressed.
Decompressing this with a general zlib compatible routine show 14 bytes of output:
00 00 00 00 00 00 00
00 00 00 00 00 00 00
where each first byte is the PNG row filter (0 for both rows), followed by 2 RGB triplets (0,0,0), for the 2 lines of your image.
"Without using any libraries" you need 3 separate routines to:
read and parse the PNG superstructure; this provides the IDAT compressed data, as well as essential information such as width, height, and color depth;
decompress the zlib part(s) into raw binary data;
parse the decompressed data, handling Adam-7 interlacing if required, and applying row filters.
Only after performing these three steps you will have access to the raw image data. Of these, you seem to have a good grasp of step (1). Step (2) is way harder to "do" yourself; personally, I cheated and used miniz in my own PNG handling programs. Step 3, again, is merely a question of determination. All the necessary bits of information can be found on the web, but it takes a while to put everything in the right order. (Just recently I found an error in my execution of the rarely used Paeth row filter--it went unnoticed because it is fairly rarely used in 'real world' images.)
See Building a fast PNG encoder issues for a similar discussion and Trying to understand zlib/deflate in PNG files for an in-depth look into the Deflate scheme.

In-memory layout of array in Turbo Pascal

We have an old application in Turbo Pascal which can save its internal state into a file, and we need to be able to read/write this file in a C# application.
The old application generates the file by dumping various in-memory data structures. In one place, the application just dumps a range of memory, and this memory range contains some arrays. I am trying to noodle out the purpose of the bytes immediately preceding the actual array elements. In particular, the first two items in the block can be represented as:
type
string2 = string[2];
stringarr2 = array[0..64] of string2;
string4 = string[4];
stringarr4 = array[0..64] of string4;
In the data file, I see the following byte sequence:
25 00 02 02 41 42 02 43 44 ...
The 25 is the number of elements in the array. The 02 41 42 is the first string element, "AB"; the 02 43 44 is the second string element, "CD", and so on. I don't know what the 00 02 between the array element count and the first array element refers to. It's possible the array element count is 25 00 and the element size is 02, but each array element is actually 3 bytes in size.
In the place in the file where the array of 4-character strings starts, I see the following:
25 00 04 00 00 04 41 42 43 44 04 45 46 47 48
Again, there's the 25 which is the number of elements in the array; 04 41 42 43 44 is the first element in the array, "ABCD", and so on. In between there are the bytes 00 04 00 00. Maybe they are flags. Maybe they are some kind of indication of the shape of the array (but I don't see how 02 and 04 both indicate a one-dimensional array).
I don't have access to Turbo Pascal to try writing different kinds of arrays to a file, and don't have authorization to install something like Free Pascal, so my opportunities for experimentation along those lines are very limited.
These arrays are not dynamic, since Turbo Pascal didn't have them.
Thanks in advance for any dusty memories.
Pascal arrays have no bookkeeping data. You have an array of five-byte data structures (string[4]), so an array of 65 of them occupies 65*5=325 bytes. If the program wrote more than that, then it's because the program took special measures to write more. The "extra" values weren't just sitting in memory that the program happened to write to disk when it naively wrote the whole data structure with SizeOf. Thus, the only way to know what those bytes mean is to find the source code or the documentation. Merely knowing that it's Turbo Pascal is no help.
It's possible that the first section of the file is intentionally the same size as all the other array elements. For the two-character strings, the "header" is three bytes, and for the four-character strings, the "header" is five bytes, the same as the size of the strings. That would have allowed the program to use a file of string4 data type for the file, and then just skip the file's first record. The zero between the file length and the string length in the header might belong to either of those fields, and the remaining two zero bytes might just be filler.
Besides the layout of the individual strings of characters in the file, you will also need to consider what code page those single-byte characters are from. C# chars are unicode 2 byte chars.
If you're lucky, the original file data contains only ASCII 7 bit characters, which covers characters of the English alphabet. If the original data contains "European" letters such as umlauts or accented characters, these will be "high ascii" char values in the range 128..255. You'll need to perform an encoding conversion to see these characters correctly in C#. Code page 1252 Windows Latin 1 would be a good starting point.
If the original file data contains Japanese, Chinese, Korean, Thai, or characters from other "Eastern" scripts, you have a lot of work ahead of you.
Turbo Pascal strings are prefixed with a length byte. So a string[2] is actually 3 bytes: length, char1 and char2. An array of string[2] will hold all the strings one by one directly after each other in memory. If you do a blockwrite with the array as a parameter it will immediately start with the first string, it will not write any headers etc. So if you have the source you should be able to see what it writes before the array.