Hope I'm understanding the question(s) correctly here...
Quote: "I can't write my 64-bit encryption key to a custom file because I'm having trouble keeping track of pointers,"
Here is a quick tutorial explaining pointers. May give you a different take:
` ~MY LITTLE POINTERS TUTORIAL~
`
`
`
` Featured Voice Performers
`
` nonZero as Raindrops Thundermuffin
// Err, yeah. Too many references.
// Okay, so you wanna know about pointers? You really wanna know? For starters, in DBP, you'll have less
// to worry about since we don't have much in the way of memory access. So, let's get the basics (you prolly know
// this but we'll start here anyhow:
PRINT "Section 1"
PRINT ""
myPTR AS DWORD // A pointer is a DWORD
// DWORD range is roughly 4,300,000,000
// This variable holds the adress of the FIRST BYTE in a range of bytes forming a block of memory
myPTR = MAKE MEMORY(512) // So here we've just assigned THE START POINT of block of 512 bytes to the variable myPTR
FILL MEMORY myPTR, 65, 1 // Place a value of 65 at the adress stored in myPTR
PRINT myPTR // This'll print a funky-looking number, but not what is stored there because this variable
// holds a simple value. That value is an adress in memory.
PRINT *myPTR // Oh my gosh, oh my gosh, oh my gosh, now it pronted 65!
// That "star" or asterisk next to a variable tells the machine to read the value
// stored in that variable and then look for the matching adress in memory.
PRINT CHR$(*myPTR) // Now this will print A because ASCII character 65 happens to be uppercase a.
WAIT KEY
CLS
// Now we'll move on
PRINT "Section 2"
PRINT ""
FILL MEMORY myPTR, 80, 1 // We'll fill these first 3 bytes with PIE
FILL MEMORY myPTR, 73, 1
FILL MEMORY myPTR, 69, 1
PRINT myPTR // The funky numbers again
PRINT myPTR + 1 // Notice how they relate?
PRINT myPTR + 2 // Because, obviously, they are sequential.
PRINT ""
PRINT *myPTR // So if this works,
PRINT *myPTR + 1 // So should this... But it won't. Instead of 73, you'll get 74.
PRINT "" // You requested the number that myPTR points to, then you added 1.
// You need to request the next memory location. You need to do this instead:
PRINT *myPTR
nextPTR = myPTR + 1 // So nextPTR is the address 1 greater than myPTR...`
PRINT *nextPTR // Bingo, 73
INC nextPTR
PRINT *nextPTR // As expected, 69
// Sadly, we cannot do this in DBP "PRINT *(myPTR+1)" but you can in other languages
WAIT KEY
CLS
// Now we'll move on to something more advanced
DELETE MEMORY myPTR // This is very important. When you create memory, you MUST delete it when you are done
myPTR = 0 // and you really SHOULD set the pointer value to 0 when you delete the memory. Otherwise
nextPTR = 0 // you'll be trying to read from an area of memory you no longer control.
// I'm going to do something here to illustrate the dangers of not setting a pointer to 0 (NULL)
badPTR AS DWORD
badPTR = 4123456 // Making up an adress
PRINT "Byte at badPTR: ";
PRINT *badPTR // Prolly prints garbage. But thar garbage may be vital memory. Okay, so no harm done by just
// having a peek. But what if this was not commented out:`
// DELETE MEMORY badPTR
// FILL MEMORY badPTR, 65, 1
// Imagine writing over important data? Imagine deleting it?
myPTR = MAKE MEMORY(32) // We'll allocate 32 bytes of memory now. Remember we freed the previous 512 and set the pointer
// to zero.
cChar AS BYTE
FOR offset = 0 TO 33 // We will now iterate from the memory start point. Remember that we're going to use an offset
cAddr = myPTR + i // and that means we'll be adding a value to the address so to fill the first byte, the offset
cChar = 65 + offset // must be zero. Btw cChar starts at A or 65.
FILL MEMORY cAddr, cChar, 1
NEXT offset // So we filled the memory with A-Z and some garbage.
// Wanna check? Let's do that but do it a different method. This is not the sanest way to handle
// memory and potentially dangerous but it will optimize code somewhat.
DEC myPTR // What?Decreasing it? Yep.
FOR i = 1 TO 26 // 26 letters in the alphabet. In this case we won't be using an offset.
INC myPTR // The first cycle places myPTR back on the first byte of the memory chunk we allocated.
PRINT *myPTR; // Each subsequent cycle increases the value myPTR holds by 1, moving it along the memory chunk.
NEXT i
DEC myPTR, 26 // Now this is why this method is dangerous. If we don't restore our pointer to its exact value,
// it may mess up future operations. Suppose we ran the FOR-loop above now. We'd overrun the memory
// chunk and write to memory that wasn't ours! Or what if we deleted myPTR without restoring its
// position? All the bytes behind it wouldn't get deallocated which would be a possible memory leak.
// In other words, use with caution. It's generally better to not alter your original pointer and
// instead, copy it to a temp variable and do the alterations on that.
WAIT KEY
CLS
END
// Sorry, that's all we have time for. It's the fundermentals and the importants optimisations, no-no's and concepts.
Quote: "I want to store it in the 64 bits it actually requires, not the 1024 that DarkBASIC writes the array to. "
Now, you bit problem: The bit-size in arrays doesn't matter because you'll convert all the data to bytes instead.
1 byte = 8 bits on all architecture (that I know of), however ints, floats, doubles, etc. are a different kettle of fish which is why I always try to work with raw bytes where possible. So how DBP stores arrays won't really matter since you're not going to be using the arrays in their current form. In short yes, my tutorial specifically works with bytes only.
Quote: "Thing is, I want all of my files to turn into my new custom file type, .enco. To avoid any confusion between file types, I want to have the start of the file read out as the unencrypted name of the file."
About your header issue, you can encrypt the body separately. Here's my file-containers tut in rough, first draft:
// Simplify, simplify, simplify.
// There's no need to know the FILE SIZE. This is because if you know the PADDING SIZE, you're good to go.
// Consider:`
// File of 1024 bytes
// If weknow the header and footer are 200 bytes each, we know that:
// a) The file is 624 bytes of data. b) The reading begins at an offset of 200 bytes and ends at 824th byte (200+624)
GLOBAL myFIle$: myFile$ = "C:\Test.bmp"
GLOBAL destFile$: destFile$ = myFile$ + "__OUTPUT_PostOp." + RIGHT$(myFile$, 3)
DIM header(399) AS BYTE: FOR i = 0 TO 399: header(i) = RND(255): NEXT i // Generate meaningless data for illustration
DIM footer(199) AS BYTE: FOR i = 0 TO 199: footer(i) = RND(255): NEXT i
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// M A K E A F I L E C O N T A I N E R //
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
myBlock = 1: myFile = 1 // Naughty, don't use same-name variables, eg: myFile$ vs myFile
OPEN TO READ myFile, myFile$
MAKE MEMBLOCK FROM FILE myBlock, myFile // Load fileinto memory
CLOSE FILE myFile
// Okay, got a memblock of the file. We also have our header and footer. So basically, we'll weld them together
// and start our read from the 401st byte and end at the position [end - 200].
// There's an optional extra if you wish to make headers that vary in size.
memSize = GET MEMBLOCK SIZE(myBlock) // We need the size of the file first
memPTR AS DWORD
memPTR = GET MEMBLOCK PTR(myBlock) // As before we'll use the pointer (I shall explain pointers in
headerSize = 400 // We know this
footerSize = 200 // We know this too
GOSUB Optional_Part1 // FTR: I hate GOSUB but it makes life easier for this example
// Now we fuse the file into one.
myBlock2 = 2 // Start with creating an image in memory...`
newMemSize = headerSize + footerSize + memSize
MAKE MEMBLOCK myBlock2, newMemSize // Creating a memblock big enough to accomodate it all
newPTR = GET MEMBLOCK PTR(myBlock2) // Get the pointer
DEC headerSize // Temporarily decrease for this op
FOR i = 0 TO headerSize
tmpPTR = newPTR + i // As demonstrated
tmpVal = header(i) // <--- Why this is necessary is one of those DBP mysteries but we can't
// just use an array, ie: FILL MEMORY tmpPTR, header(i), 1
// My guess is that even though I declared header() AS BYTE, it's being stored as INT
FILL MEMORY tmpPTR, tmpVal, 1 // Put header into memblock byte-by-byte
NEXT i
INC headersize // Restore post-op
INC newPTR, headerSize // Shift the pointer to its new position
DEC memSize // as before
FOR i = 0 TO memSize
tmpPTR = newPTR + i
tempPTR = memPTR + i
FILL MEMORY tmpPTR, *tempPTR, 1 // Place file into memblock byte-by-byte
NEXT i
INC memSize
INC newPTR, memSize
DEC footerSize
FOR i = 0 TO footerSize
tmpPTR = newPTR + i
tmpVal = footer(i)
FILL MEMORY tmpPTR, tmpVal, 1
NEXT i
INC footersize
DELETE MEMBLOCK myBlock // Cleanup time
memPTR = 0
tmpPTR = 0
tempPTR = 0
memSize = 0
headerSize = 0
footerSize = 0
IF FILE EXIST(destFile$) = 1: DELETE FILE destFile$: ENDIF
OPEN TO WRITE myFile, destFile$ // Ready to write image to file
MAKE FILE FROM MEMBLOCK myFile, myBlock2 // Write image to file
CLOSE FILE myFile
DELETE MEMBLOCK myBlock2 // Final cleanup
newPTR = 0
DIM header(0)
DIM footer(0)
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// R E A D A F I L E C O N T A I N E R //
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// In this example, we know the size of the header and footer. However, there's also provision for cases where we don't in
// the 2nd optional bit, aptly named Optional_Part 2.
OPEN TO READ myFile, destFile$ // This process again
MAKE MEMBLOCK FROM FILE myBlock, myFile
CLOSE FILE myFile
memPTR = GET MEMBLOCK PTR(myBlock) // Pointer
memSize = GET MEMBLOCK SIZE(myBlock) // Full size
// Get the sizes of the header and footer
headerSize = 400 // If they're constant and you know them
footerSize = 200
GOSUB Optional_Part2 // If they vary and you need to know them
newSize = (memSize - headerSize) - footerSize // Parenthesis for illustrative purposes, however it's good to avoid ambiguity all the same
MAKE MEMBLOCK myBlock2, newSize // The new size is derived from the total sans the header and footer
newPTR = GET MEMBLOCK PTR(myBlock2) - 1 // Yup, that's correct. This is a trick when iterating.
tmpFrom = memPTR + headerSize // Pay attention to these lines of code, they're complex.
tmpTo = tmpFrom + newSize // You can avoid this (and prolly should) but I'm lazy :D
FOR i = tmpFrom TO tmpTo
INC newPTR // INC newPTR will bring the address to position 1 on first iteration and each byte forward thereafter
FILL MEMORY newPTR, *i, 1 // Remember i is now a pointer to and addreww in myBlock.
NEXT i // Told you it was a cheap trick.
DEC newPTR, newSize // Reposition pointer to its original value (not that we need to, this is just illustrative)
DELETE MEMBLOCK myBlock // Cleanup
memPTR = 0
memSize = 0
tmpFrom = 0
tmpTo = 0
headerSize = 0
footerSize = 0
// Write the output to a new file
finalFile$ = destFile$ + "FINAL." + RIGHT$(myFile$, 3)
IF FILE EXIST(finalFile$) = 1: DELETE FILE finalFile$: ENDIF
OPEN TO WRITE myFile, finalFile$
MAKE FILE FROM MEMBLOCK myFile, myBlock2 // Write our extracted file for comparison
CLOSE FILE myFile // Last cleanup
DELETE MEMBLOCK myBlock2
newPTR = 0
newSize = 0
EXECUTE FILE finalFile$, "", ""
END
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// S U B R O U T I N E S //
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Optional_Part1:
// So you want varying-sized headers and footers right. Well, I'll throw in a simple method that'll give you
// around 65kb (65000bytes) on either side.
tempByte1 AS BYTE // You should always declare variable types but in DBP it's lenient, however
tempByte2 AS BYTE // you should ALWAYS declare BYTEs and DWORDs
// Header first
tempByte1 = FLOOR(headerSize / 255) // <-- Tells us how many times 255 goes into the size
tempByte2 = headerSize MOD 255 // <-- Tell us the remainder when the header size is divided by 255
// Thus we store the size in only 2 bytes (I do it in my function library too)
header(0) = tempByte1 // Place the two values in the 1st and 2nd index of the header array.
header(1) = tempByte2
// Now footer
tempByte1 = FLOOR(footerSize / 255) // <-- Tells us how many times 255 goes into the size
tempByte2 = footerSize MOD 255 // <-- Tell us the remainder when the header size is divided by 255
// Thus we store the size in only 2 bytes (I do it in my function library too)
header(2) = tempByte1 // Place the two values in the 3rd and 4th index of the header array.
header(3) = tempByte2
// For the math in practice, see the next optional bit. I'll summarise it here as a formula:`
answer = (255 * tempByte1) // Gives us the first piece. If the size was < 255 this will equate to 0
answer = answer + tempByte2 // Second part is added since it was the remaineder
// EG1: answer = ((0 x 255) + 200) = 200 bytes
// EG2: answer = ((1 x 255) + 125) = 400 bytes
RETURN
Optional_Part2:
// So you wanted adjustable head and footer sizes right. Well now we'll be getting to the usage. We assume only
// that you know the layout of your file's header.
tempByte1 = *memPTR: INC memPTR // We're gonna do it the quick way now so take note.
tempByte2 = *memPTR: INC memPTR
headerSize = (255 * tempByte1) + tempByte2 // As previously discussed
tempByte1 = *memPTR: INC memPTR
tempByte2 = *memPTR: DEC memPTR, 3 // We decrease the pointer adress back to the original position
footerSize = (255 * tempByte1) + tempByte2 // As before
// We now have the derived header and footer size.
RETURN
^ Basically, while stored in a memblock, the "REAL" part of your file can be encrypted any way you like. Once committed to the final memory image (the memblock containing your file, the header and the bloating), that memory image is written byte-for-byte to a file. As in my tutorial, you could use the first 2 bytes to represent the header size. Then you're free to add whatever normal data you like upto 65000 bytes (65kb) worth (you can make it bigger but it'll cost more bytes). You can also use footers, as demonstrated, so you will be able to better secure your data by adding a few hundred bytes of "rubbish" or storing information about how to decrypt it.
Now about XORing: It is easy to crack under the right circumstances (ie the key is not as long as the data). So random or not, if the sequence repeats, it can be deciphered. This is not to say XOR cannot be used for encryption, merely that complex mechanisms are required (which is why I opt for mixing things up or simply using addition and subtraction + byte-swapping). Here is an example I cooked up (It's purely for illustrative purposes):
// Incorrect XORing can be weak encryption
// Create our example elements
key64bit AS DWORD
key64bit = MAKE MEMORY(8) // 1 byte = 8 bits .: 8 bytes = 8 x 8 bits = 64 bits
FILL MEMORY key64bit, 1, 1
FILL MEMORY key64bit, 15, 1
FILL MEMORY key64bit, 8, 1
FILL MEMORY key64bit, 37, 1
FILL MEMORY key64bit, 19, 1
FILL MEMORY key64bit, 124, 1
FILL MEMORY key64bit, 253, 1
FILL MEMORY key64bit, 1, 1
INPUT "Some text, my good sir> ", txt$
tLen = LEN(txt$)
IF tLen < 8: txt$ = "ABCDEFGHIJKLMNOPQRSTUVWXYZ": ENDIF
tLen = LEN(txt$)
CLS
PRINT "Plain Text Message:"
PRINT txt$
PRINT ""
myPTR AS DWORD
myPTR = MAKE MEMORY(tLen)
msgSecret AS DWORD
msgSecret = myPTR
DEC msgSecret
FOR i = 1 TO tLen
INC msgSecret
FILL MEMORY msgSecret, ASC(MID$(txt$, i)), 1
NEXT i
msgSecret = myPTR
// Okay, we've our necessary text and XOR cipher. We'll encrypt it in a typical way
txtByte AS BYTE
xorByte AS BYTE
DEC msgSecret
DEC tLen
PRINT "Encrypted Message:"
FOR i = 0 TO tLen
INC msgSecret
tmp64 = key64bit + (i MOD 9)
txtByte = *msgSecret
xorByte = *tmp64
txtByte = txtByte ~~ xorByte
PRINT CHR$(txtByte);
FILL MEMORY msgSecret, txtByte, 1
NEXT i
INC tLen
msgSecret = myPTR
PRINT ""
Choice:
PRINT ""
PRINT "1) Test Decrypting"
PRINT "2) Test Cracking"
PRINT ""
INPUT "Make your choice:", ch
IF ch < 1 OR ch > 2: GOTO Choice: ENDIF
IF ch = 1: GOSUB Example_One: ENDIF
IF ch = 2: GOSUB Example_One: ENDIF
PRINT "": PRINT "Press Any Key..."
WAIT KEY
DELETE MEMORY myPTR
myPTR = 0
msgSecret = 0
END
Example_One: // Example 1 - Decrypt it to show we can
PRINT "Decrypt Message:"
DEC msgSecret
DEC tLen
FOR i = 0 TO tLen
INC msgSecret
tmp64 = key64bit + (i MOD 9)
txtByte = *msgSecret
xorByte = *tmp64
txtByte = txtByte ~~ xorByte
PRINT CHR$(txtByte);
FILL MEMORY msgSecret, txtByte, 1
NEXT i
INC tLen
msgSecret = myPTR
PRINT ""
RETURN
Example_Two: // Example 2 - weakness in XORing
PRINT "Crack Message:"
DEC msgSecret
DEC tLen
FOR i = 0 TO tLen
INC msgSecret
txtByte = *msgSecret
txtBtye = txtByte << 8 // Key length is essentially 8 chars or 64bits
txtByte = txtByte ~~ txtByte
PRINT CHR$(txtByte);
FILL MEMORY msgSecret, txtByte, 1
NEXT i
INC tLen
msgSecret = myPTR
PRINT ""
RETURN
// Okay basically then, I merely have to ascertain your key length and I can derive decrypt
// your message .: the only thing I endeavor to do is find your key length. Now, I can either
// simply brute-force attack your XORed encryption with varrying lengths until I get it, or,
// in extreme cases, I can rather analyse the encrypted information and search for patterns.
// The patter-search is kinda complex and I don't fully understand it all (so many methods and
// maths it'll feel like a repeat of high school) so that research is up to you.
...I just think I beat the record for post-length in terms of chars. Hope there aren't any bugs or glitches in this mess.