These new MicroChunks...

Discussion around programming R'n'D, its source code and its tools.

Moderators: Flumminator, Zomis

Post Reply
Zomis
Posts: 1502
Joined: Mon Jun 21, 2004 1:27 pm
Location: Sweden
Contact:

These new MicroChunks...

Post by Zomis »

I looked a little at the source code of 3.2.0-8, thinking that "Oh, this new CUSX chunk seems interesting"... and well, what can I say...? It truly is veeery interesting... and for me a bit difficult to understand...

However, I think I start to understand the concept... although I gotta say it's pretty advanced...

But what about these "good old" version checks? the chunk_config_CUSX_change array won't look the same from version to version, right? So when loading a level from an older version of RND, how can RND know which values to skip?

If I'm not totally mistaken, the following chunks are the only ones which use the MicroChunks: INFO, ELEM, NOTE, CUSX, GRPX, am I right?

Please Holger, a little explanation would be nice... :)
Then we'll see if I can make a 3rd party program using these new micro chunks, sigh* I probably can do so the day I understand the micro chunks and know how to implement such in Delphi...

Anyways, I gotta say that the MicroChunks are a great idea, and is probably something I'll use in my own programs more often in the future :)

Btw, why is there a comma at the end of line 230 and 231? (in files.c)
Zomis
Posts: 1502
Joined: Mon Jun 21, 2004 1:27 pm
Location: Sweden
Contact:

Post by Zomis »

Okay... I now understand the backwardcompatibility, you're saving a little id for each value to know which value is which.
But why is the value 40-something for 16-bit values, and 80-something for 32-bit values? Isn't it enough with 20-something and 40-something? There won't be 40 values which needs 16 bits, will it?

It's amazing to see that you're having a variable size on the LevelFileConfigInfo entries.. I wonder how I can solve that in Delphi... otherwise I have to do a workaround by filling it with some useless values.

But I don't understand all those optional values, what's the point in copying some data, for example? I see that it's commonly used with CEs... so what's the difference between the xx_ei and yy_ei in those cases?

And why do you need an empty LevelFileConfigInfo at the end of each chunk_config_****? Doesn't C know when the array is finished otherwise?

Like I said before, really nice technique. Once I know how to implement them it will make 3rd party programs much much much easier!

At first when I thought about this new technique, I thought it was consuming a little bit too many bytes, but now when I see how many bytes that is saved on those default values, wow! :D I like this style more and more :)
User avatar
Holger
Site Admin
Posts: 4073
Joined: Fri Jun 18, 2004 4:13 pm
Location: Germany
Contact:

Post by Holger »

> the chunk_config_CUSX_change array won't look the same from version
> to version, right?

That's right (and by intention)! That the whole flexibility aspect, with upward and downward compatibility!

> So when loading a level from an older version of RND, how can RND know
> which values to skip?

Well, this direction is pretty easy: Even if the order of micro chunks would not be allowed to be arbitrary, you could do this with code that would skip those values that did not exist in older versions. But this is not needed, as the loader code is able to identify each micro chunk, and can make a lookup in the micro chunk table! This is especially important for the other direction: Old versions of R'n'D loading level files written by newer versions of R'n'D! This works the same way, and just as with the "normal" IFF style chunks: Unknown micro chunks are simply skipped, while everything that is known is loaded correctly!

For micro chunks, this will only work for R'n'D versions starting with 3.2.0, of course. (Especially, pre-3.2.0 versions of R'n'D won't be able to read post-3.2.0 level files anymore, so the update from 3.1.2 to 3.2.0 will be highly recommended when 3.2.0 is released.)

> If I'm not totally mistaken, the following chunks are the only ones which
> use the MicroChunks: INFO, ELEM, NOTE, CUSX, GRPX, am I right?

Yep. I've just added a little bit of compatibility code for the "CONF" chunk (which was renamed to "ELEM" and uses different value positions now). This chunk should not be written anymore, but was replaced by "ELEM". Compatibility support for "CONF" is also not needed by 3rd party programs, as it was only used in pre-release versions. I only added that support for Alan Bonds "color cycle" level set and for Darkon's "Step Puzzles"... :-)

> Please Holger, a little explanation would be nice...

Yep -- see below!

> Btw, why is there a comma at the end of line 230 and 231? (in files.c)

They are both (and also a few others) completely redundant -- I've just removed them. Although they do not hurt (and are ignored by the C compiler), you are right that they should not be there. :-)

> I now understand the backwardcompatibility, you're saving a little id for
> each value to know which value is which.

Yep! That's the whole trick... :-)

> But why is the value 40-something for 16-bit values, and 80-something
> for 32-bit values? Isn't it enough with 20-something and 40-something?
> There won't be 40 values which needs 16 bits, will it?

You're right, but you have forgotten an important thing: The loader needs to know how many bytes to read! And you don't want to detect/store this for each number in the code, but want to use a more generic way. Therefore, each "number field" is build like the following:

Bits 7,6: which kind of data type is stored in the micro chunk
Bits 5..0: which unique data type number is stored for this element

This way, you can store 64 different values of each data type for each element!

The data type that is stored in bit 6 and 7 is the following (from src/files.c):

/* values for chunks using micro chunks */
#define CONF_MASK_1_BYTE 0x00
#define CONF_MASK_2_BYTE 0x40
#define CONF_MASK_4_BYTE 0x80
#define CONF_MASK_MULTI_BYTES 0xc0

So you can see that this mask value for the two highest bits of this byte store which kind of data type is stored in that micro chunk. The remaining six lowest bits are then simply used to number through all different values of this data type, giving 64 different values for each data type.

The "multi byte" micro chunk then needs an additional field with the number of bytes to store there, which I limited to 16 bits (so you can store up to 65536 bytes in a multi byte micro chunk, which is enough -- for everything that uses more than 65536 bytes, you should use a dedicated IFF style chunk, anyway!).

> It's amazing to see that you're having a variable size on the
> LevelFileConfigInfo entries.. I wonder how I can solve that in Delphi...

Don't panic -- it's not really variable size. It's just that the static initialization only uses those values that matter, while the rest are just left away (and are automatically initialized with zeroes by the C compiler). This is just to make things more readable and skip as many unused values as possible (and not to type too much when programming... ;-) ).

> otherwise I have to do a workaround by filling it with some useless
> values.

That's exactly what the C compiler does -- if you do it manually, you get exactly the same result. So no problem here... :-)

> But I don't understand all those optional values, what's the point in
> copying some data, for example? I see that it's commonly used with
> CEs... so what's the difference between the xx_ei and yy_ei in those
> cases?

This looks indeed ugly (and could probably solved better). This is completely unneeded for loading and saving micro chunks, so you can ignore it for now.

It's used to copy all those different CE values inside the level editor (when you use copy/cut/paste), to prevent to much redundancy which usually results in errors (adding a new CE property and adding it to the loading/saving code, but forgetting to add it to the copying code).

> And why do you need an empty LevelFileConfigInfo at the end of each
> chunk_config_****? Doesn't C know when the array is finished
> otherwise?

You're more or less right here! C does not know anything about arrays at all, in fact -- it only knows pointers. (The C compiler knows something, but that's all...) For static arrays, I could use "sizeof()" here to determine the size of the initialized array, and divide by the size of the array itself, but this would be horrifying ugly and potentially error-prone, and won't work with dynamically allocated arrays at all.

Therefore, I just add one "empty" entry at the end of each such "list", and loop through that list until the loop will be stopped by detecting that "empty" entry. This is a good solution to prevent those famous buffer overflow errors that happen all the time in C if you don't take care! :-)

> At first when I thought about this new technique, I thought it was
> consuming a little bit too many bytes, but now when I see how many
> bytes that is saved on those default values, wow!

Yep -- most CE levels get significantly smaller when loaded and saved again using the new micro chunks, as most values are still at their default values. The only problem is if you change the default values later -- but this can be solved relatively easy with some compatibility code...
Zomis
Posts: 1502
Joined: Mon Jun 21, 2004 1:27 pm
Location: Sweden
Contact:

Post by Zomis »

>This way, you can store 64 different values of each data type for each
>element!

Ah, I see... and there are 4 different kinds of data sizes: 8 bit, 16 bit, 32 bit, and multibyte. And 256/4=64 :) Then I see why 64 is a good number here.

>Don't panic -- it's not really variable size. It's just that the static
>initialization only uses those values that matter, while the rest are just
>left away (and are automatically initialized with zeroes by the C
>compiler). This is just to make things more readable and skip as many
>unused values as possible (and not to type too much when
>programming... ;-) ).

I don't panic, I ask :P Ah, I see... and I just found out that Delphi handles this the exact same way, but in Delphi I have to write "element: -1; save_type: SAVE_CONF_ALWAYS", and so on... but it's ok to leave some stuff out. Which is good... however, I can't declare it as constant in Delphi because of the pointers. So I have to use a little workaround, which redefines chunk_config_CUSX each time it is called (that is, each time the xx_ei pointer changes).

Speaking about that, I was wondering...

Code: Select all

xx_ei = *ei;  /* copy element data into temporary buffer */
...
*ei = xx_ei;
There's something suspicious here... you copy all the data from ei to xx_ei, right? At first I was thinking that you just changed the pointers. But that's not it, because 1. xx_ei is defined as static. And 2. if you changed the pointer, I don't see any reason for this: *ei = xx_ei;
That's how you handle the pointer problem I encountered in Delphi. Your xx_ei is static since it always point to the same place, but it's dynamic since it can change the value, clever! Too bad Delphi doesn't like that idea..

>It's used to copy all those different CE values inside the level editor
>(when you use copy/cut/paste), to prevent to much redundancy which
>usually results in errors (adding a new CE property and adding it to the
>loading/saving code, but forgetting to add it to the copying code).

Ah, I see... and now when I look more into it, it seems like those new structures are being used much more than I originally thought. Certainly not a bad idea! Both being used for copying and setting to default as well, nice!

>You're more or less right here! C does not know anything about arrays
>at all, in fact -- it only knows pointers. (The C compiler knows
>something, but that's all...) For static arrays, I could use "sizeof()" here
>to determine the size of the initialized array, and divide by the size of
>the array itself, but this would be horrifying ugly and potentially
>error-prone, and won't work with dynamically allocated arrays at all.

Oh, that's right. Completely forgot about that, but now that you mention it I remember. C really doesn't have a clue about arrays. But in what way the compiler has it, that I wonder? Or maybe you mean that it optimizes all the pointer-code so that it actually acts like an array? In any case, I agree with you. Having a NULL-structure is much better in that case than a bunch of sizeof() checks.

>Yep -- most CE levels get significantly smaller when loaded and saved
>again using the new micro chunks, as most values are still at their
>default values. The only problem is if you change the default values later
> -- but this can be solved relatively easy with some compatibility code...

Hmm... changing the default values later... that's right... I wonder how that compatiblity code would look like? Let's say the default time changes from 100 to 120, how would that look in the source? Just wanted to know so I could get an idea about how it can be done in Delphi ;)
My guess though is that you do something like the following:

Code: Select all

  {
    -1,					SAVE_CONF_ALWAYS,
    TYPE_INTEGER,			CONF_VALUE_16_BIT(3),
    &li.time,				(level->game_version >= VERSION_IDENT(3,2,0,9) ? 120 : 100)
  },
User avatar
Holger
Site Admin
Posts: 4073
Joined: Fri Jun 18, 2004 4:13 pm
Location: Germany
Contact:

Post by Holger »

> And 256/4=64
> Then I see why 64 is a good number here.

Yep. The main advantage is that you can just mask out the upper two and/or lower six bytes, and then just treat them independently (instead of, for example, checking if that value is between 64 and 127 (or 128 and 191 etc.) and then knowing that this is one of 64 values of a certain data type).

> There's something suspicious here...
> you copy all the data from ei to xx_ei, right?

Yes, correctly. That's the only way that I can specify the pointers to the single structure members in the configuration list -- it has to be static. So I just copy back and forth from/to a static structure. Just as you described. :-)

> Both being used for copying and setting to default as well, nice!

Ah, yes, setting to defaults before loading the partial data from the micro chunks, right! Before, I had to write ugly code that used the same structure fields again and again for (at least) the following tasks:

- setting all structure members to their default value
- saving them to a level file
- loading them from a level file
- copying them inside the level editor

At least four places where always the same structure members must be used. Far too much redundancy. Forgetting one or two of them in one or several of these tasks is not obvious due to the sheer number of structure members and can therefore lead to errors that are hard to find. The current solution lists all needed data once in a big list and uses it for all tasks. I like it much better than the previous, inelegant solution. :-)

> > and divide by the size of the array itself

Oops. I meant "by the size of the structure", of course. But you got that right, anyway.. :-)

> But in what way the compiler has it, that I wonder?

Not too much either -- it just knows about the size of the static list, and about the size of that structure (therefore sizeof() works), but it does know nothing about the size of dynamically allocated memory to which a pointer of that type may point at runtime.

> Hmm... changing the default values later... that's right...
> I wonder how that compatiblity code would look like?
[...]
> My guess though is that you do something like the following:
[...]
&li.time,
(level->game_version >= VERSION_IDENT(3,2,0,9) ? 120 : 100)
[...]

Well, this won't work, because although "&li.time" is known at compile time, "level->game_version" isn't, and therefore cannot be used to initialize a static structure definition. But you're nearly right! I would use two structure entries: One with the old and one with the new default version, together with an additional structure member to indicate which value to use with which level version, so that the right value is used at the right places. (And with an indication which value to use for saving actual level files and which not, which actually exists with the field that is already initialized with values like SAVE_CONF_ALWAYS, for example.)
Zomis
Posts: 1502
Joined: Mon Jun 21, 2004 1:27 pm
Location: Sweden
Contact:

Post by Zomis »

>Not too much either -- it just knows about the size of the static list, and
>about the size of that structure (therefore sizeof() works), but it does
>know nothing about the size of dynamically allocated memory to which a
>pointer of that type may point at runtime.

Well, at least that's more than nothing. It all sounds reasonable. No compilers know anything about the size of the dynamically allocated memory, eh? That sounds like a basic behind "dynamical allocation" if I'm not mistaken.

>Well, this won't work, because although "&li.time" is known at compile time,
>"level->game_version" isn't, and therefore cannot be used to initialize a
>static structure definition. But you're nearly right! I would use two structure
>entries: One with the old and one with the new default version, together
>with an additional structure member to indicate which value to use with
>which level version, so that the right value is used at the right places. (And
>with an indication which value to use for saving actual level files and which
>not, which actually exists with the field that is already initialized with values
>like SAVE_CONF_ALWAYS, for example.)

Oh, that's true. Didn't thought about level->game_version not being avalible in compile time.
So there'll be more structure members to the structure? But what if you'll someday change the default value again? Do you need to make more structure members then? It sounds like non-static structures has an advantage here, because then you could change the default value depending on the version, without the need of more structure members. (No, I'm not telling you to switch to non-static structures, if I could have use that static structure in Delphi, I definetly would have used it!)


I just found something which I don't understand completely...
files.c, lines 2909-2914

Code: Select all

	  int *element_array = (int *)(conf[i].value);
	  int j;

	  for (j = 0; j < num_entities; j++)
	    element_array[j] =
	      getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
I'm especially having problems with the first line. Something similar is happening on line 2918 as well,

Code: Select all

struct Content *content= (struct Content *)(conf[i].value);
What is done exactly? Why is there a * after int and Content in these lines? I know it's has something to do with pointers, but at the moment I'm way too confused to understand it completely.


Did you come up with this "MicroChunk" idea yourself? Or where did you learn about it?
User avatar
Holger
Site Admin
Posts: 4073
Joined: Fri Jun 18, 2004 4:13 pm
Location: Germany
Contact:

Post by Holger »

> No compilers know anything about the size of the dynamically allocated
> memory, eh? That sounds like a basic behind "dynamical allocation" if I'm
> not mistaken.

Literally spoken, you're right, of course. ;-)

But that was not what I meant (although it was what I wrote ;-) ) -- the interesting point is the following: Do I know at runtime how big is my dynamically allocated list? In C, the answer is always "no" (unless I store it somewhere by myself whenever I allocate or free list entries, of course), because C does not know at all what a "list" is -- it just knows that it has a pointer, pointing at some memory segment, and that this memory should be interpreted in a certain way.

With Perl, it would be completely different, for example. Although you start with a list of some size (or an empty list), and later add and delete list entries, you can determine at any time how many entries are in that list. The same is true with C#, or with Java (I think -- I'm not a big Java programmer). The difference is that these languages use a concept that is called "managed code" etc. -- the runtime environment knows at any given time how much memory is consumed in your list, and how many entries it has (unless you do nasty things, of course, and then it's your own fault if it bites you ;-) ).

Therefore, most problems of this type (those famous buffer overflow bugs, causing 90% of all actual security problems in well-known software) occur in C type languages (especially C and C++). That's why we have to take special care when we do such things. (In Perl, this would all be much, much easier than in C. :-o )

> But what if you'll someday change the default value again? Do you need
> to make more structure members then?

Yep. One entry for each default value that existed at some time.

> It sounds like non-static structures has an advantage here, because then
> you could change the default value depending on the version, without the
> need of more structure members.

Doesn't make a big difference -- you would still have to provide all these different default values, and then apply the matching one to your (static or dynamic) config list.

The following works better -- imagine our default value again:

version, value:

"3.2.0", 110
"3.2.1", 120
NULL, 130
...

The "version" entry that is "NULL" is always our current default value (for the last, current version). If the version is older than 3.2.0, we use 110. If not, but it's older than 3.2.1, we use 120. If not, we use 130. (We wouldn't use strings for the version number, of course).

Having it that way (stored in that list) makes things very easy to understand by just looking at that list. If we assume that only a few default values change from time to time, the list won't grow that big so that we would get a performance problem. (And if it does, we would transform it to a hash structure once before applying it to value checks in our loops.)

> int *element_array = (int *)(conf.value);
[...]
> struct Content *content= (struct Content *)(conf.value);

> What is done exactly? Why is there a * after int and Content in these
> lines?

This only means "interpret the 'void' (typeless) pointer as a pointer to an 'int' type or as a pointer to a 'struct Content' type. As we don't know before which type of data is stored in the micro chunk, we must start with a "void" pointer and cast it to the real data type as soon as we know what it is!

> Did you come up with this "MicroChunk" idea yourself?

Well, yes ...

> Or where did you learn about it?

I just thought to myself: "How can I store all these individual, small values without (a) storing them in a fixed-length chunk (like "CUS4") which permanently runs out of free bytes when adding new features, and without (b) using "real" (IFF style) chunks that are way to big to store a single byte and an element number (because of the eight-byte overhead for the chunk identifier (4 bytes) and chunk length (another 4 byte) -- that would be bigger than the data to store), and that would either need different chunk identifiers or additional data bytes to distinguish the different values from each others."

Then that idea of variable-length "micro chunks" inside usual IFF style chunks came to my mind. I think it's practical, but only time will tell... :-)
HerzAusGold
Posts: 362
Joined: Sun Sep 25, 2005 4:41 pm
Location: Germany

Post by HerzAusGold »

Nice - Microchunks.
I read this thread carefully - but what would you store in these microchunks in the future.
May be I should improve my english.
And the answer is ... 42 !
User avatar
Holger
Site Admin
Posts: 4073
Joined: Fri Jun 18, 2004 4:13 pm
Location: Germany
Contact:

Post by Holger »

> [...] but what would you store in these microchunks in the future.

Well, besides the already existing element (normal and custom) properties (like player speed or yamyam content), I will store all possible future element properties that you could think of, like custom yamyam speed (if this should be added one day).

The good thing about these micro chunk technique is that I don't have to change the level format anymore, but only have to add the new customizable properties to the config list, and voila! It gets stored in the same IFF chunk without modifications and without wasted bytes "for future use"! And older versions of R'n'D that recognize micro chunks (that is, starting with 3.2.0) can read all properties that they recognize (and skip those that they don't know of)! :-)
Zomis
Posts: 1502
Joined: Mon Jun 21, 2004 1:27 pm
Location: Sweden
Contact:

Post by Zomis »

>Literally spoken, you're right, of course. ;-)

Of course I am :D ;)

>With Perl, it would be completely different (...)

With Delphi as well, of course ;) I see now how C is different when it comes to pointers and lists.

>Doesn't make a big difference -- you would still have to provide all these
>different default values, and then apply the matching one to your (static
>or dynamic) config list.

But the big difference is that if you use a dynamic config list, you don't have to add extra structure members to it. It sounds like a little waste if you add some structure members for the differing default values when there's in reality only a few config lists that use that value, the others automatically set it to the default value.

>Having it that way (stored in that list) makes things very easy to
>understand by just looking at that list. If we assume that only a few
>default values change from time to time, the list won't grow that big so
>that we would get a performance problem. (And if it does, we would
>transform it to a hash structure once before applying it to value checks
>in our loops.)

You're right that the default values probably (and hopefully) won't change much. But since you declare a new structure member, then isn't there memory allocated for it even for those config lists which doesn't use the new structure member at all? Sounds like a memory waste. Even though most computers can handle it, and it probably is only an extra 500 bytes that is allocated, it's still 500 bytes. ;)

By the way, I found out that I actually can make the config list constant/"static" in Delphi. Probably I used a pointer's structure earlier, now it is the real structure I'm using. Works fine.

>This only means "interpret the 'void' (typeless) pointer as a pointer to an
> 'int' type or as a pointer to a 'struct Content' type. As we don't know
>before which type of data is stored in the micro chunk, we must start
>with a "void" pointer and cast it to the real data type as soon as we
>know what it is!

Ah, I see... I was suspecting something like that, just were sooo confused in my head earlier (after reading some source and learning the MicroChunk concept) that I couldn't understand it.

>> Did you come up with this "MicroChunk" idea yourself?
>Well, yes ...
Very impressive, then! :)

>Then that idea of variable-length "micro chunks" inside usual IFF style
>chunks came to my mind. I think it's practical, but only time will tell... :-)

Good thinking! :D


I've now completed my first Delphi program which loads MicroChunks from a file. (Not RND-related ones, only some test values). I haven't tried saving anything yet though, but I don't think that should be so hard (my guess is that the saving part is much easier, but we'll see...)
The only strange things that occour to me is that when I load a multi-byte element array, the element array becomes bigger than necessary. And if I try to truncate it I get an Access Violation error. If I don't truncate it then it works as expected. So in this case I follow the rule "Don't fix what isn't broken" and ignore that abnormality.
User avatar
Holger
Site Admin
Posts: 4073
Joined: Fri Jun 18, 2004 4:13 pm
Location: Germany
Contact:

Post by Holger »

> But the big difference is that if you use a dynamic config list, you don't
> have to add extra structure members to it.

That's right, of course. But how do you add the "right" entries to that dynamic list? By using an "if-else" cascade for each element and each different default element for different versions (to decide which one must be added to the dynamic list, and to skip the rest)? Storing this statically and just checking one more structure member (game version) makes it nice and easy! :-)

> You're right that the default values probably (and hopefully) won't change
> much. But since you declare a new structure member, then isn't there
> memory allocated for it even for those config lists which doesn't use the
> new structure member at all? Sounds like a memory waste.

You're exactly right! Even now, there is already quite a "big" waste of memory in this micro chunk config list -- most entries are either at default values 99% of the time, or not used at all (although the memory as allocated anyway and silently set to zero).

> Even though
> most computers can handle it, and it probably is only an extra 500 bytes
> that is allocated, it's still 500 bytes.

Yep. But IMHO it's absolutely worth it just for the clarity and readability of the code (and using much less redundancy of code now), and shouldn't hurt modern systems too much anyway. Most parts of R'n'D's code is a big mess, so I'm always happy if I can replace it with something better (more generic and more readable, therefore far better maintainable). So I think it's worth the additionally wasted memory... :-)
Zomis
Posts: 1502
Joined: Mon Jun 21, 2004 1:27 pm
Location: Sweden
Contact:

Post by Zomis »

Well, I guess you're right. For keeping the code clean and so it's definetely worth spending some extra bytes in memory on default values. I'm not much better when it comes to saving memory...:)

I'm not totally sure how you'll handle changing default values but I guess I'll just see it when it becomes actual :) I just hope you won't encounter any problems with these new microchunks... they're the best! (When you finally get them to work without Access Violations of course...)
User avatar
Holger
Site Admin
Posts: 4073
Joined: Fri Jun 18, 2004 4:13 pm
Location: Germany
Contact:

Post by Holger »

Well, most problems that I (hopefully) have solved with these new micro chunks now (like flexibility and extensibility) would be commonly solved using XML files these days.

It's just that I still couldn't stand using XML files for R'n'D levels! :-o ;-)

Especially the BODY chunk (and other raw data chunks) cannot be anything other than just plain ugly in XML, I think... ;-)

But for most other things (like saving n CEs with each having m change pages), XML files would indeed be an even more elegant solution (and as an extra you would get full buzzword compatibility for free =:-) )...

But for a "classic" short and compact binary data format, these new micro chunks should serve us well enough... :-)
Zomis
Posts: 1502
Joined: Mon Jun 21, 2004 1:27 pm
Location: Sweden
Contact:

Post by Zomis »

>Well, most problems that I (hopefully) have solved with these new micro
>chunks now (like flexibility and extensibility) would be commonly solved
>using XML files these days.

Flexibility and extensibility problems!? May I ask if those problems also affects 3rd party programs trying to load RND levels?

>It's just that I still couldn't stand using XML files for R'n'D levels! :-o ;-)

Yes, I also totally dislike XML (part of that disliking could be because I haven't learned to handle it in Delphi, of course...:P)
Maybe XML would make the CE changing more beautiful, but I can just imagine what the BODY chunk would be like... yikes :shock:

>But for a "classic" short and compact binary data format, these new micro chunks should serve us well enough... :-)

Oh yes, I'm very impressed of them :)
Post Reply