Technische Anmerkungen

Allgemeine Datentypgrößen

Je nach Interpretation unterscheidet man zwischen signed (vorzeichenbehaftete) und unsigned (vorzeichenlose) Datentypen. Vorzeichenbehaftete Typen können durch das Zweierkomplement negative Werte darstellen auf Kosten der minimalen und maximalen Wertebereiche. Vorzeichenlose Typen können nur positive Werte darstellen bei Ausnutzung des maximalen Wertebereichs.

Signed (Vorzeichenbehaftet)

Bezeichnung Bits Min. Wert Max. Wert 32-Bit C/C++ Java
BYTE 8 -128 127 char byte
WORD 16 -32768 32767 short short
DWORD 32 -2147483648 2147483647 long int
FLOAT C/C++ 32 +/- 1,1*10-38 +/- 3,4*1038 float
FLOAT Java 32 +/- 1,4*10-45 +/- 3,4*1038 float


Hinweis: Die Java FLOAT Werte basieren auf der strictfp Darstellung, d.h. diese Werte sind unter jeder Java Virtual Machine identisch! Die C/C++ FLOAT Werte hingegen sind implementierungsabhängig und sollten als Annäherungswert verstanden werden (die Werte stammen von meinem XP Pro SP2 System unter gcc 3.4.2 MinGW-Edition auf einem AMD X2 4600+).

Unsigned (Vorzeichenlos)

Bezeichnung Bits Min. Wert Max. Wert 32-Bit C/C++ Java
BYTE 8 0 255 unsigned char
WORD 16 0 65535 unsigned short
DWORD 32 0 4294967295 unsigned long


Die Spritespezifikation von Half-Life benutzt nach Sichtung der von Valve zur Verfügung gestellten C/C++ Quellcodes ausschließlich für integrale Werte die signed Datentypen, auch wenn nur positive Werte gespeichert werden.

Die Spezifikation ist so ausgelegt, daß in C/C++ quasi immer Blöcke zu 4 Bytes pro Headereintrag gelesen/geschrieben werden müssen. Dadurch wird das unvermeindliche Byte Alignment vom Compiler umgangen und eine konsistente Struktur erreicht.

Hinweis: In Java gibt es keine unsigned Typen.

Hexadezimale Zahlenwerte

Wer mit hexadezimalen Zahlenwerte wie beispielsweise 0x2C nichts anfangen kann, der sollte hier diesen Crashkurs lesen.

In der Informatik werden Zahlenwerte neben dem dezimalen Zahlensystem auf Basis 10 auch in den drei folgenden Zahlensystemen Binär (Basis 2), Oktal (Basis 8) und Hexadezimal (Basis 16) betrachtet.

Das Hexadezimalsystem kennt neben den zehn Dezimalziffern 0 bis 9 auch sechs Zusatzsymbole, die dem lateinischen Alphabet entnommen worden sind. Diese lauten schlicht A bis F. Die nachfolgende Tabelle zeigt, welchen Wert welches Symbol besitzt. Zum Vergleich sind auch die oktalen und binären Werte aufgeführt:

Dezimal Hex Oktal Binär
0 0 0 0
1 1 1 1
2 2 2 10
3 3 3 11
4 4 4 100
5 5 5 101
6 6 6 110
7 7 7 111
8 8 10 1000
9 9 11 1001
10 A 12 1010
11 B 13 1011
12 C 14 1100
13 D 15 1101
14 E 16 1110
15 F 17 1111

Dezimal nach Hexadezimal umrechnen

Die Dezimalzahl 1234dec soll nach Hexadezimal umgerechnet werden. Der Algorithmus dazu lautet:

  • Teile die Dezimalzahl durch 16
  • Schreibe den größten ganzzahligen Teilerwert auf
  • Merke dir den Restwert
  • Teile nun den Teilerwert der vorhergehenden Division durch 16
  • Verfahre solange, bis der Teilerwert 0 ist


1234 / 16 = 77 Rest 2
77 / 16 = 4 Rest 13
4 / 16 = 0 Rest 4

  • Man reiht nun die Restwerte von unten nach oben auf: 4, 13, 2
  • Die Zahl 13 wird laut Tabelle als Hexwert D dargestellt: 4, D, 2
  • Nun zieht man alle Werte zusammen und hat seine Hexadezimalzahl: 4D2hex = 1234dec

Hexadezimal nach Dezimal umrechnen

Nun kehren wir den Vorgang um und machen aus 4D2hex wieder 1234dec. Der Algorithmus hierzu lautet:

  • Fange rechts bei der „kleinsten“ Hexzahl an
  • Multipliziere die Hexzahl mit 16e
  • Setze für den Exponenten e anfänglich den Wert 0 ein
  • Merke dir das Ergebnis der Multiplikation
  • Gehe eine Hexzahl nach links und erhöhe e um 1
  • Verfahre so mit allen Hexzahlen
  • Addiere am Ende alle Ergebnisse zusammen


4*162 + D*161 + 2*160
4*162 + 13*161 + 2*160
4*256 + 13*16 + 2*1
1024 + 206 + 2
1234dec

Info: Wenn man anstelle der 16 eine 8 einsetzt, kann man oktal umrechnen bzw. mit einer 2 binär umrechnen.

Hexadezimale Notation

In C/C++ und anderen Programmiersprachen, die sich syntaktisch an C/C++ anlehnen, wird eine hexadezimale Ganzzahl wahlweise mit einem vorangestellten 0X oder 0x gekennzeichnet. So auch hier in der Spezifikation.

Little Endian und Big Endian

Zahlenwerte können in einem Prozessor auf zwei Arten interpretiert werden: Einmal als Little Endian und einmal als Big Endian. Bei Little Endian Prozessoren (z.B. Intel, AMD) wird das niedrigste Byte an der niedrigsten Speicheradresse abgelegt, während bei Big Endian Prozessoren (z.B. Motorola, SUN Sparc, PowerPC) das höchste Byte an der niedrigsten Speicheradresse abgelegt wird. Das ist die technische Betrachtung der Bytespeicherung, wenn man den Speicher als langes Array mit der Leseweise links → rechts betrachtet.

Als Beispiel dient die Hexzahl 0xAABBCCDD:

Modell Byte 1 Byte 2 Byte 3 Byte 4
Little Endian DD CC BB AA
Big Endian AA BB CC DD


Die informatische Betrachtung dagegen liest immer von rechts → links, weshalb hier eine andere Anordung erscheint:

Modell Byte 4 Byte 3 Byte 2 Byte 1
Little Endian AA BB CC DD
Big Endian DD CC BB AA


Folgender C/C++ Code beispielsweise liest einen 4 Byte unsigned int immer in Hostbyteorder, unabhängig von Hostbyteorder und der Byteorder der Daten:

static inline uint32_t read_ulong(const uint8_t *p)
{
    return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
}

Spezialfall Java

Java arbeitet intern immer im Big Endian Format. Das gilt auch für low level Arbeiten! Auf Little Endian Systemen wie Intel/AMD PCs ist das natürlich sehr unvorteilhaft. Das byteweise Lesen/Schreiben ist natürlich völlig unabhängig und ist auf beiden Formaten identisch. Aber sobald man Vielfache von Bytes verarbeitet, muß man wissen, mit welchem Format der Computer arbeitet und entsprechende Gegenmaßnahmen ergreifen.

In Java kann man das Format des Computers auslesen, indem man folgenden Code nimmt:

import java.nio.*;
 
ByteOrder bo = ByteOrder.nativeOrder();
 
if(bo == ByteOrder.LITTLE_ENDIAN)
{
    // Little Endian
}
else
{
    // Big Endian
}

Die Bytestreams von Java (mit der Basisklasse InputStream bzw. OutputStream) arbeiten zwar byteweise, aber wie gesagt im Big Endian Format. Die sehr hilfreichen Klassen DataInputStream und DataOutputStream für schnelles byteweises Lesen und Schreiben von Binärdaten können daher auch nur Big Endian und sonst nichts. Das muß man bedenken, denn ein Sprite, welches über DataOutputStream auf einem Intel/AMD PC geschrieben worden ist, ist intern auch Big Endian und nicht mehr Little Endian. Per Hexeditor kann man das nachprüfen, indem man ein Originalsprite lädt und es mit den Binärdaten des Java-gespeicherten Sprites vergleicht.

Java stellt aber auch keine Methode bereit, das Format des Streams umzuschalten, so daß man nur entweder die eine Möglichkeit hat, alle notwendigen Bytestreamklassen abzuleiten und spezielle Little Endian Bytestreams zu programmieren oder die Argumente an die Methoden der Bytestreams sofort nach Little Endian umzuschreiben.

Das Umschreiben von Datentypen in Java geschieht per Bitoperationen. Hier mal ein Beispiel, wie man ein short in Java von Big Endian auf Little Endian umpolt:

public short littleEndianShort(short s)
{
    return (s >>> 8) | (s << 8);
}

Ältere Java VMs, die den > > > Operator nicht kennen, müssen dagegen so arbeiten:

public short littleEndianShort(short s)
{
    return ((s >> 8) & 0x7F) | (s << 8);
}

Sonstige Programmiersprachen

An dieser Stelle möchte ich auch Programmierer anderer Sprachen dazu ermuntern, diese Spezifikation sinnvoll zu erweitern. Bisher findet man hier nur Beispiele der Sprachen C, C++ und Java. Wünschenswert wären auch andere Populärsprachen wie Delphi oder C#. Wichtig wäre außerdem, daß etwaige nichtstandardisierte Sprachen wie Pascal oder BASIC-Dialekte immer mit entsprechenden Hinweisen der verwendeten Compiler/Interpreter ausgezeichnet werden. Zum Beispiel existieren in Standard-Pascal nur vier Datentypen (integer, real, char und boolean), während in anderen Pascal-Varianten durchaus auch mehr Datentypen vorkommen können.

Die Verwendung aller Dokumente einschließlich der Abbildungen ausschließlich zu nichtkommerziellen Zwecken. Verbreitung des Dokuments auf Speichermedien, (insbesondere auf CD-ROMs als Beilage zu Zeitschriften und Magazinen oder sog. "Mission-Packs" etc.) ist untersagt.
 
allgemeines_gamedesign/artikel/specification_keys.txt · Zuletzt geändert: 2009/02/26 01:42 von Kriz