For @morningtoast
EDIT: This is a quick example of how to pack data (here, tables) inside of cart memory instead of the code editor, and then load it from there during runtime. To that you can reduce the compressed code size of your cart.
It's not really proper compression, though. If you want to see that, check out this instead: https://www.lexaloffle.com/bbs/?tid=3930
super thanks. i'll dig in and try to understand what's going on and how to use it.
Memory addresses are still a little confusing...like how is "1a" 26 after 0000? So each value in the original table takes up a byte within memory?
So if I want to use map memory at 0x2000 and have a table with 100 values in it, that would run from 0x2000 to 0x2100...but I'm guessing not.
Morning Toast, get ready to learn a whole new kind of number!
ah hell.
gonna need to find a hex calculator then. something I can do 0x2000 + 100 and get the resulting hex.
okay, so I got this working and understand it enough, I think.
But when I have it store data to 0x2000 map memory, it saves it in pixels on the first tab of the sprite sheet.
what's the trick to tell it where to save those pixels on the sprite sheet? I have game sprites on tabs 1,2,3 but nothing on 4. Can I tell it to save specifically to the "bottom" of the sprite sheet? How do you calculate what address those are at?
If you're on Windows, do WIN+R then type 'calc' and press enter.
Alt+3 is the hotkey for Programmer mode.
In p8, the notation for hex is done by adding a prefix of "0x" to a number (like in that cart).
Btw my example cart doesn't do any 'real' compression since it's just moving data into cart memory vs program editor memory so it isn't adding to your compressed size.
But @zep has already made something that does that AND actually compresses the data: https://www.lexaloffle.com/bbs/?tid=3930
probably another "duh" thing to ask...but while using your example works great with a table of many single character strings, when I try to save longer strings it doesn't seem to work.
storing/pulling this data set works great
mytable={"a","b","c","d"} |
this is 4 bytes, right?
but then using this one does not
mytable={"abc","def","ghi","jkl"} |
is this 4 bytes or is 12 bytes?
I think in my mind I'm trying to treat memory as a sort of array but that's not quite it, is it?
So each byte is a value from 0-255
They're made up of 8 bits.
Each of those bits can encode a 1 or 0 value (true or false). But by combining them, you can encode larger numbers. You know how in decimal we go..
0, 1, 2, 3, 4, 5, 6, 7, 8, 9...
and then we increment the 10's place and clear the 1's place to start over?
10, 11, 12, 13, 14, 15.. etc
Another way to say '15' is
1 x 10 + 5 x 1
^--how many digits we count with, to the power of 'how many places to the left of the decimal'
Same idea with binary:
0=0000
1=0001
2=0010
3=0011
4=0100
5=0101
6=0110
7=0111
8=1000
...
15=1111
Hex works the same way, except instead of 0..9 or 0..1 we count from 0..F in each place. A byte capable of holding a number less than or equal to the highest you can count to in 2 hex places:
00
...
FF
F x 16 + F x 1 --> ( 15 x 16) + (15 x 1) --> 240 + 15 --> 255
which in binary would be:
1111 1111
Anyway, long story short is that if you were only storing 16 unique values, you could encode 2 of them per byte; each would use 4 bits of the byte. But since you have more than that (26 letter + 10 numeric digits + whatever) you can only store one character per byte.
For your other question -- To modify that compression thing to instead work on a table of strings you would want to do something like this:
function cmp(data,addr) if not addr then addr=0x0000 end counter=0 for i=0,#data-1 do //input each character in the string in order.. for chr=1,#data[i+1] do c=sub(data[i+1],chr,chr) value=str2byte(c) poke(addr+counter,value) cstore(addr+counter,addr+counter,1) counter+=1 end end return addr+counter end |
I haven't tested that but I think it should work. The idea is that you want to add each character, from each string, from each item in the passed table (in order), without overwriting any of the previously written data. We track the counter separately here since if you just used 'i' then it would update a set of values in memory, then go back to one value after the first value it wrote and overwrite what you had just written, etc, etc.
Oh yeah, you'd need to rewrite the unpacking function too.. The thing there is that the above structure for /storing/ only works if either: 1.) every string is the same length, OR 2.) you put a value in there somewhere to mark it as the 'end' of that string.
So you could do something like this instead..
function cmp(data,addr) if not addr then addr=0x0000 end counter=0 for i=0,#data-1 do //input each character in the string in order.. for chr=1,#data[i+1] do c=sub(data[i+1],chr,chr) value=str2byte(c) poke(addr+counter,value) cstore(addr+counter,addr+counter,1) counter+=1 end cstore(addr+counter,addr+counter,0xff) counter+=1 end return addr+counter end |
And then when you load, you'll know that if you find a value of 255, then that string is finished. And you'll want to modify the 'len' you pass in the original packing script I put in that cart to instead be a guide for how many '255' values you hit before you know all the strings for that table have been loaded.
I was afraid of that. i probably have too much string text in my game to make good use of the memory storage. Hmmmm...
I might just have to rethink some of my in-game text and just reduce what I can.
Ima keep digging into this though...it's good new stuff to learn and will come in handy at some point.
Hey if it's dialogue then another option is to save the text as words and compress it that way. Start with the largest words and save those as value 128 or whatever. And then if it's a low number you just pull the character back in. But if it's a high number then you use a key to pull in 'hippopotamus' or whatever.
Or if you're just doing characters, then instead of 255 to end a string, you could set the high bit if it's the last character of a string. This way you aren't wasting a byte to say "that last byte was the last character.."
Without going into all that though.. In theory you wouldn't need to store a lot of your data away to drop 3k. Not all of it. Just figure out how much you'd have to get out of the editor to make it fit under the compressed code limit and then work towards that.
If you need help feel free to hit me up on twitter.
yeah, it's in-game text and messages...lots of it, more than I usually do. I'm well under in tokens and characters, just that damn compressed number.
So if you're saying to store each different word into memory...that's a ton of words, seems like there wouldn't be enough room.
If the string is
"new life form detected" |
You're saying store "new" "life" "form" "detected" each as a separate string rather than the whole thing. Then pulling each word via key to build the message in-game...? So my message display call would be something like showmsg(1,2,43,23) or something like that...? Hmmmm...interesting...
I've been stripping down strings and removing dupes. I've shaved off about a 1k but shy of renaming all my vars by hand, I'm not sure how to refactor much more.
And actually...I just took out all the in-game text strings and my game is still over the compress limit so this goes beyond just offloading text...but every little bit helps.
My Tale of Two Cities demo was designed for exactly the case where a game contains a large number of text strings, such as an adventure game. It uses a post-processing workflow to look for string literals marked with a star (*"my string") in the source code, extract them, store them in a compressed dictionary in cart data, and replace the original marked literals with function calls that locate and unpack the string.
This also adds the code to the cart to do the decompression. Whether it's worth the effort to do this to strings in an existing game that's close to the size limit, I don't know. It's just a tech demo, not a polished tool. :)
Hmmm...sounds worth a look, thanks for the pointer. I'm not above using post-processing. I tried the p8tool minimizer as a post-proc but it gets hung up on something and my cart doesn't come out working, which was a bummer.
It's frustrating given my code otherwise is well below limits and I'm getting outdone by strings and long variables names...sigh...
I'd enjoy a bug report if p8min is breaking a cart! https://github.com/dansanderson/picotool/issues
Hm. I might have to see if this is feasible for my title/attract routines.. They tend to take up a considerable chunk of my tokens. Basically store all the text bits and coordinates used for my LINE drawing bits in the MAP table. Mostly not using that space anyways.
[Please log in to post a comment]