| Structure of the program Updated 28 Apr 2009 | Upper levels: - QuArK Information Base - 4. The Source Code

4.3. Structure of the program | [ Prev - Up - Next ]

Index

- - QObjects (15 May 2007)
- - QFileObjects (15 Feb 2001)
- - Texture format (24 Feb 2008)
- - Loading files (23 Feb 2001)
- - Saving files (23 Feb 2001)
- - Displaying files (28 Apr 2009)

QObjects | Armin Rigo - 15 May 2007 | [ Top ]

The most central part of QuArK is its internal objects on which everything else is based. These objects are represented by all the classes that directly or indirectly inherit from QObject, defined in qobjects.pas. Here are the main rules about QObjects. Although the class QObject has got a few fields, these fields should be regarded as used by QuArK's internal management only. All important data must be stored using a combination of the two main features of QObjects : their Specific/Args and their subobjects (called subelements in the code). Specific/Args are similar to the ones of Quake entities : it is a list of pairs of strings. The first string, the Specific, is an arbitrary name for a setting; the corresponding second string gives the value of this setting. Objects also each have a list of subobjects. This lets objects be organized in a hierarchy. The basic principles are that the Specific/Args and the subobjects contain all the information, and that this object hierarchy is central for all operations : to load a file, we actually convert it into an object or a hierarchy of objects; to save a file, we convert the objects back to the destination file format; we can only display and work with objects, never directly with files.

QFileObjects | Armin Rigo - 15 Feb 2001 | [ Top ]

QFileObject is an abstract class that inherits from QObject. The general rule is that classes that inherit from QFileObject can be saved to or loaded from disk files, whereas classes that inherit from QObject only are internal objects whose purpose is only to be used as subobjects of other objects. For example, here is how a map file is internally organized. The ".map" file type itself is managed by a class called QMapFile that inherits from QFileObject, whereas inside the map, the hierarchy of polyhedrons, groups, entities, etc. is represented by a hierarchy of objects that inherit from QObject, because there is no point in saving these objects as single files. Graphically :
    Map object
     +-- worldspawn
          +-- polyhedron
          +-- polyhedron 2
          +-- group
               +-- entity
               +-- etc
The links in this diagram mean "is a sub-object".

Texture format | DanielPharos - 24 Feb 2008 | [ Top ]

Image data, or textures, are stored in specifics of objects, or Pixel Set Descriptors (PSD) QuArK's internal image format. There are three specifics: Image# (Where # is a number) Pal Alpha Usually, there's only one image per texture, so there is only an Image1. If the texture doesn't contain any alpha-color data, Alpha doesn't exist. If the picture isn't a paletted image, like a pcx file, Pal doesn't exist. There are two different formats that QuArK uses internally, and every loaded image is converted to those formats on load: 24-bit non-paletted, and 8-bit paletted with a 24-bit palette. The Image-specific is stored in a way Windows can understand directly. The details of it might seem a bit weird, but this is the way Windows works (this gave Windows extra speed back in the old 486-days). All the data in the Image-specific must be aligned on a 32-bit boundary per scanline. What does this mean? A scanline is simply a row of pixels as displayed on the screen. These are picture-width pixels in a single row. However, the amount of bytes used per row must be a multiple of 4 (32 bits = 4 bytes of 8 bits each). So there is a padding amount of empty, unused, erroneous bytes at the end of each row. You can set them to any value, since they won't be displayed, but making them zero's is probably nicest. How to calculate the amount of padding bytes, you ask? The amount of bits used in a single row of pixels is: picture-width x bits per pixel. The reason for the 8 bits each is because RGB images have a Decimal numbering for each Red, Green, Blue channel, or color component, that ranges from 0 to 255, or 256 digits, where pure white would be 255, 255, 255. Alpha would also be an additional component which we are skipping over for right now. But the point is, you can see how cumbersome that would be to store, as well as take more bits in memory. So to overcome this they use what is known as the Hex or Hexadecimal numbering system instead. This system has a total of 16 digits, 0 through 9 plus A, B, C, D, E AND F. Each pixel is an RGBA, Red, Green, Blue and Alpha (which may not be used) component. Each of those components gets 2 bits for their Hex number looking something like this, FFFFFF, which again is pure white, with Alpha being overlooked for now. If an image does have an Alpha component then its Hex number would look like this FFFFFFFF, 2 bits per component x 4 RGBA components = 8 bits each per pixel. Pretty slick how that all works, isn't it? A good example is to look at this Color Converter. Bits per pixel is usually a number like 1, 2, 4, 8, 16, 24 or 32. This now gives the amount of bits per line. We need to round this up to the closest multiple of 32 bits. The simplest way is by adding an additional 31, then dividing by 32 and rounding down the result. This will give the amount of bytes per scanline. To find the amount of padding bytes, simply subtract the amount of bytes used in a single row of pixels: padding = picture-width x bits per pixel - amount-of-bits-per-scanline But be careful: the scanlines are stored upside-down. This means that the first bytes you read in, are actually the first pixels of the LAST line to display! (The picture is stored vertically mirrored, or flipped!) Just do a simple picture-height - line-I-want to get the correct number of the scanline. Since you know the amount of bytes per scanline (nicely aligned, so no half-byte locations here!), you can find the start of the scanline by a simple multiplication. Since QuArK uses only 8 bits or 24 bits internally, there are only two possibilities. Check for the existance of the Pal-specific: if it's there, you're looking at an 8-bit texture, if not, then it must be a 24-bit texture. If Pal does exists, then this is a list of 256, 24 bit colors, stored in a 32-bit format: RGB (Red, Green, Blue, no Alpha here). So each color is 4 bytes in size. Color 0 is the first color in the list, etc. Alpha contains any Alpha data there might be. This is just a list of alpha-color values, no padding involved.

Loading files | Armin Rigo - 23 Feb 2001 | [ Top ]

Here are comments that will help you in writing support for a new file type. QuArK has got a complex file management structure, but you need not be aware of it as long as you use the "official" way of loading and saving files. Doing so will let QuArK handle things such as loading or saving directly inside a .pak file, and load-on-demand. To support a new file type, a class inherits from QFileObject and overrides some key methods. The first one is TypeInfo, which should return the file extension. QuArK recognize files based on their extension only, and then invokes the correct class' `LoadFile` method. This method must process the file it receive in a parameter and turn *all* information it contains into Specific/Args and subobjects. For example, the method `LoadFile` of QMapFile parses the map file and creates the whole hierarchy of groups, polyhedrons and entities. Some file types are suitable for load-on-demand, which is implemented through subobjects whose class also has a `LoadFile` method. For example, when loading a .PAK file, it is of course not completely loaded into memory : the directory of the file is processed, and folders and subfolders are created accordingly. The files inside the folders are also created but this time through the method `LoadedItem` , which may be called from a `LoadFile` method only. It creates a subobject which is marked as "not loaded yet"; this subobject's own `LoadFile` method will be called when necessary to actually load the (sub-)file. In summary, the method `LoadFile` must : load every information contained in the file and store it all as Specific/Args and subobjects, with one exception : calls to `LoadedItem` , which does not immediately load the data; don't do anything else (no direct disk access, no display, etc.), with one exception : calls to LoadSibling, to access files besides the one currently loading (typically, Quake 2's models and sprites refer to .pcx files for the model skin and sprite images).

Saving files | Armin Rigo - 23 Feb 2001 | [ Top ]

Saving files is the exact inverse of loading them. The method `SaveFile` must turn the Specific/Args and the subobjects structure back into a file. If the `LoadFile` method used LoadSibling, `SaveFile` calls Info.WriteSibling (see QkMdl.pas).

Displaying files | Armin Rigo - 28 Apr 2009 | [ Top ]

Files are never displayed directly in QuArK : only internal objects can be. This means that the display code must only rely on the Specific/Args and eventually on the subobjects hierarchy to display the content of a file. The general rule about display is that subobjects are visible in a tree hierarchy and the user can edit them freely (add subobjects, rename or delete some, move them around, and so on). For simple objects (typically, the ones with no subobject), the display form gives users access to all parameters of the object. For example, an image object is displayed in a form whose bottom gives parameters such as the size and the color depth of the image. Semi-complex objects, paradoxally, have simpler display forms : their information is stored as subobjects and the user can edit them without any particular form. For example, a sprite object is basically just a list of images, so that it is represented as an object with images as subobjects. Editing them for the user is quite easy : all he has to do is rename, move, add, delete images. A particular sprite form is needed only for the stuff that cannot be edited this way, e.g. how long each image is to stay in the sprite animation sequence. Really-complex objects (e.g. maps or models) require more sophisticated display code, which only gives a preview of the object when users click on it in the QuArK Explorer and invokes a complex editor when needed. Note that for the subobjects of an object to be displayed in tree views, you need to override the `IsExplorerItem` method. This method controls whether each subobject is displayed or not, or whether it is allowed to be dropped there by the user or not. In general it should be implemented as follows :
    function xxx.IsExplorerItem(Q: QObject) : TIsExplorerItem;
    begin
     Result:=ieResult[(Q is someclass) or (Q is someotherclass) or...];
    end;

Copyright (c) 2009, GNU General Public License by The QuArK (Quake Army Knife) Community - http://quark.sourceforge.net/ | [ Prev - Top - Next ]