Hulpmiddel voor structuren

Algemeen

Het hulpmiddel Structuren schakelt analyseren en bewerking van byte-arrays in gebaseerd op door de gebruiker gemaakte structuurdefinities, die kunnen worden gebouwd uit arrays, unions, primitieve typen en een lijst met waarden.

Het heeft een eigen instellingendialoog, die bereikt wordt de knop Instellingen. Er zijn verschillende opties die ingesteld kunnen worden, zoals de stijl waarin (decimaal, hexadecimaal of binair) de waarden getoond worden. Verder is het mogelijk te kiezen welke structuurdefinities geladen moeten worden en welke structuren getoond worden in het voorbeeld.

Structuren worden gedefinieerd in Okteta-structuurdefinitiebestanden (gebaseerd op XML, met de bestandsextensie .osd). Verder kan een .desktop bestand metagegevens bevatten over dat structuurbeschrijvingsbestand, zoals auteur, homepagina en licentie.

Op dit moment is er geen ingebouwde ondersteuning voor maken of bewerken van structuurdefinities, dit moet daarom handmatig gedaan worden zoals beschreven in de volgende secties.

Structuurdefinities installeren

Met KNewStuff installeren

De gemakkelijkste manier om nieuwe structuurdefinities te installeren is door de ingebouwde KNewStuff-ondersteuning in Okteta te gebruiken. Om een bestaande structuur te installeren opent u de instellingendialoog van het hulpmiddel Structures. U selecteert daar het tabblad Beheer van structuren en drukt op de knop Nieuwe structuren ophalen.... De dialoog die verschijnt stelt u nu in staat om structuren te installeren en te deïnstalleren.

Structuurdefinities handmatig installeren

Het hulpmiddel voor structuren zoekt naar structuurdefinities in de map okteta/structures/ van de map voor programmagegevens van de gebruiker (u kunt deze vinden via de opdracht qtpaths --paths GenericDataLocation). U moet deze map misschien nog aanmaken als er nog geen structuurdefinities zijn geïnstalleerd.

Er bestaan twee bestanden voor elke structuurdefinitie: een bestand voor de actuele definitie en een .desktop-bestand voor de metagegevens (auteur, versie, etc.).

In deze map is een submap voor elke structuurdefinitie, waarin zowel het .desktop-bestand en het .osd of main.js bestand van deze definitie aanwezig zijn.

Bijvoorbeeld met de programmagegevensmap qtpaths --paths GenericDataLocation en een structuurdefinitie genaamd VoorbeeldStructuur is de map okteta/structures/VoorbeeldStructuur, die bestanden VoorbeelStructuur.desktop en VoorbeeldStructuur.osd bevat.

De nieuwe geïnstalleerde structuren gebruiken

Nadat u nieuwe structuurdefinities heeft geïnstalleerd, moet u Okteta opnieuw starten voordat u deze kunt gebruiken. Nadat Okteta opnieuw is gestart, opent u de instellingendialoog van het hulpmiddel voor structuren en selecteert u vervolgens het tabblad Beheer van structuren en controleert of de relevante structuurdefinitie is ingeschakeld. Ga vervolgens naar het tabblad Structuren en controleer dat het verlangde element aan de rechterkant is te zien.

Structuurdefinities delen

Het is waarschijnlijk niet nodig voor standaard structuren dat u de definitie zelf aanmaakt, in plaats daarvan kunt u een al bestaande definitie gebruiken van een plaats zoals store.kde.org.

U kunt ook een eigengemaakte definitie met anderen delen. Om dit te doen maakt u een archiefbestand aan (bijv. een gezipt tar-archief, .tar.gz) met daarin de map met het .desktop-bestand en het structuurdefinitiebestand. In het voorbeeld in het laatste sectie van dit hoofdstuk zal dit zijn de map ExampleStructure met al zijn inhoud. Door gebruik van dit formaat voor het delen van de structuurdefinities is het mogelijk ze automatisch in Okteta te installeren en is handmatige installatie niet nodig.

Structuurdefinities maken

Opmerking

een meer bijgewerkte, maar niet geheel gerede handleiding om structuurdefinities te schrijven is te vinden in op de KDE UserBase Wiki.

Er zijn twee verschillende manieren om structuurdefinities aan te maken. De eerste is het schrijven van de definitie in XML de andere is het gebruik van JavaScript. Het gebruik van JavaScript geeft u de vrijheid om meer complexere structuren met mogelijkheden zoals structuurcontrole te creëren. Het gebruik van XML geeft u minder mogelijkheden maar als statistische structuren alles is wat u nodig heeft dan kan dit de makkelijkste manier zijn. Als u een dynamische structuur nodig heeft bijv. waar array lengtes van andere waarden in de structuur afhangen of de structuurindeling verandert wanneer sommige waarden wijzigingen, dan zult u de structuurdefinitie in JavaScript moeten schrijven. Er is een uitzondering op die regel: als u een array heeft waarvan de lengte exact gelijk is aan een andere waarde in de structuur, dan kan u ook XML gebruiken. Maar als het iets is zoals lengte - 1 dan moet het JavaScript zijn.

Structuurdefinitie XML-bestandsformaat

Opmerking

een meer bijgewerkte, maar niet geheel gerede handleiding om structuurdefinities te schrijven is te vinden in op de KDE UserBase Wiki.

Het .osd XML-bestand heeft één basiselement: <data> zonder attributen. Binnen dit element moet er één van de volgende elementen zijn:

<primitive>

Om een 'primitive' gegevenstype zoals bijv. int en float aan te maken. Dit element accepteert geen subelementen en kan de volgende attributen hebben:

type

Het type van deze primitieve type. Het moet een van de volgende zijn:

  • char voor een 8-bits ASCII-teken

  • int8, int16, int32, int64 voor een geheel getal met teken met die grootte

  • uint8, uint16, uint32, uint64 voor een geheel getal zonder teken van die grootte

  • bool8, bool16, bool32, bool64 voor een logische waarde (boolean) (0 = false, elke andere waarde = true) van die grootte

  • float voor een 32-bits IEEE754 drijvende-komma getal

  • double voor een 64-bits IEEE754 drijvende-komma getal

<bitfield>

Om een 'bitfield' aan te maken Dit element accepteert geen subelementen en kan de volgende attributen hebben:

breedte

Het aantal bits gebruikt door dit 'bitfield'. Moet liggen tussen 1 en 64.

type

Het type van dit 'bitfield'. Het moet één van de volgende zijn:

  • unsigned voor een bitfield waar de waarde geïnterpreteerd zal worden als een waarde zonder teken (waardereeks van 0 tot 2breedte - 1)

  • signed voor een bitfield waar de waarde geïnterpreteerd zal worden als een waarde met teken (waardereeks van -2breedte - 1 tot 2breedte - 1 - 1)

  • bool voor een 'bitfield' waar de waarde als een booleaanse waarde zal worden geïnterpreteerd

Opmerking

Denk er aan om padding toe te voegen na een <bitfield>, omdat het volgende element anders in het midden van een byte zou beginnen (behalve voor tekenreeksen en arrays, omdat zij automatisch padding toevoegen). Als u dit gedrag wenst dan is padding natuurlijk niet nodig.

<enum>

Om een type primitive te maken, maar waar de waarden getoond worden, indien mogelijk, als leden van een enumeratie. Dit element accepteert geen subelementen (maar u hebt mogelijk een tag <enumDef> in het bestand nodig om er naar te refereren). Het heeft de volgende attributen:

enum

De onderliggende 'enum' voor deze waarde. Moet overeenkomen met het attribuut name van één van de tags <enumDef> in dit bestand.

type

Het type hiervan is enum. Zie type attribuut van <primitive>. Het enige verschil is dat Double en Float geen betekenis hebben.

<flags>

Dit is hetzelfde als <enum> met het enige verschil dat waarden gerepresenteerd worden als een bitwise-or van alle waarden van de enumeratie (opsomming).

<struct>

Hiermee creëert u een structuur. Alle andere elementen (inclusief een <struct>) kunnen hiervan onderdeel uitmaken (child)

<union>

Hiermee creëert u een union. In wezen hetzelfde als een <struct>, behalve het feit dat alle child-elementen vanaf dezelfde offset starten. Dit is handig voor het op verschillende manieren onderzoeken van dezelfde volgorde van bytes.

<array>

Hiermee creëert u een array. Dit element accepteert precies een child (het onderliggende type array), waarin u elk soort element kan plaatsten, zelfs een <array>. Het heeft de volgende attributen:

lengte

Het aantal elementen in dit array als decimaal getal. Als alternatief kan het ook een tekenreeks zijn waarvan de naam overeenkomt met een eerder gedefinieerde <primitive>, <enum> of <flags> element. De lengte zal dan overeenkomen met de waarde van dat element. De lengte is op dit moment beperkt tot 10000, omdat grotere arrays teveel geheugen gebruiken en teveel vertragen.

<string>

Hiermee kun u een tekenreeks in een codering naar keuze creëren. Standaard is dit een tekenreeks in de C-style beëindigt door een NULL. Maar met de volgende attributen kunt u andere soorten tekenreeksen creëren:

terminatedBy

Dit attribute bepaalt door welk unicode karakter de tekenreeks wordt beëindigd. Dit moet een hexadecimaal nummer zijn (optioneel met een leidende 0x). Wanneer de codering in ASCII is dan hebben alleen waarden tot 0x7f een betekenis. Als u zowel dit of maxCharCount of maxByteCount niet heeft ingesteld dan is dit standaard ingesteld op 0 (C-style string)

maxCharCount

Het maximum aantal tekens dat deze tekenreeks kan hebben. Als terminatedBy ook is ingesteld dan eindigt de tekenreeks door datgene wat als eerste is bereikt. Dit gaat niet samen met maxByteCount

maxByteCount

Het maximum aantal bytes dat in deze tekenreeks past. Als terminatedBy ook is ingesteld, dan wordt de tekenreeks afgesloten door datgene wat het eerst bereikt wordt. Dit gaat niet samen met maxCharCount. Alleen bij decoderingen zoals ASCII komt dit overeen met maxCharCount.

type

De codering van deze tekenreeks. Kan een van de volgende zijn:

  • ASCII

  • LATIN-1

  • UTF-8

  • UTF-16-LE of UTF-16-BE. Als geen -LE of -BE achtervoegsel is gegeven, wordt 'little endian' aangenomen.

  • UTF-32-LE of UTF-32-BE. Als geen -LE of -BE achtervoegsel is gegeven, wordt 'little endian' aangenomen.

Elk element accepteert ook een attribuut name dat dan zichtbaar is in de structurenweergave.

Een voorbeeld van een structuurdefinitie in zowel XML als JavaScript

Opmerking

een meer bijgewerkte, maar niet geheel gerede handleiding om structuurdefinities te schrijven is te vinden in op de KDE UserBase Wiki.

De gezamelijke stap gedeeld door beide benaderingen

Ons bestand met metagegevens ziet er zo uit:

        [Desktop Entry]
        Icon=arrow-up❶
        Type=Service
        ServiceTypes=KPluginInfo

        Name=Simpele teststructuur
        Comment=Een erg simpele teststructuur met slechts twee items

        X-KDE-PluginInfo-Author=Alex Richardson
        X-KDE-PluginInfo-Email=foo.bar@email.org
        X-KDE-PluginInfo-Name=simplestruct
        X-KDE-PluginInfo-Version=1.0
        X-KDE-PluginInfo-Website=https://www.plugin.org/
        X-KDE-PluginInfo-Category=structure
        X-KDE-PluginInfo-License=LGPL
        X-KDE-PluginInfo-EnabledByDefault=false
        

Het pictogram getoond in Okteta voor deze structuur kan alles zijn wat gevonden wordt door kdialog --geticon uit te voeren of een pad naar een pictogram

Deze velden zouden allemaal zichzelf moeten verklaren, behalve voor X-KDE-PluginInfo-Name. De waarde van dit veld moet overeenkomen met de naam van de map met het bestand evenals de naam van het .desktop-bestand. Bij het maken van XML-structuurdefinities zal de naam van het .osd bestand ook met de naam overeen moeten komen.

In dit voorbeeld willen we een map met de naam simplestruct met daarin het bestand simplestruct.desktop hebben. Als we de structuren definiëren in XML dan is in de map ook een bestand genaamd simplestruct.osd aanwezig. Bij gebruik van JavaScript hebben we in plaats daarvan een bestand met de naam main.js.

Een eenvoudige XML-structuurdefinitie

Om te beginnen creëren we een definitie voor een eenvoudige teststructuur met alleen integrale gegevenstypes (een char, een 32-bit signed integer en een bitfield). Dit is in C/C++ uitgeschreven als:

          struct simple {
            char aChar;
            int anInt;
            bool bitFlag :1;
            unsigned padding :7;
          };
          

De eerste stap is het schrijven van het .osd-bestand overeenkomstig het bestandsformaat zoals in de vorige sectie beschreven. We geven het de naam simplestruct.osd:


          <?xml version="1.0" encoding="UTF-8"?>
          <data>
            <struct name="simple">
              <primitive name="aChar" type="Char"/>
              <primitive name="anInt" type="Int32"/>
              <bitfield name="bitFlag" type="bool" width="1"/>
              <bitfield name="padding" type="unsigned" width="7"/>
            </struct>
          </data>
          

wat overeenkomt met de C/C++ definitie.

Maak nu een map aan met de naam simplestruct in de structuur installatiemap (lees handmatig structuurdefinities installeren) en kopieer de twee bestanden naar deze map. Na het opnieuw opstarten van Okteta kunt u de nieuwe structuur gebruiken.

De eenvoudige structuur in JavaScript

Om bovengenoemde structuren in JavaScript te implementeren, creëert u een bestand met de naam main.js in plaats van simplestruct.osd en wijzigt u X-KDE-PluginInfo-Category=structure in X-KDE-PluginInfo-Category=structure/js. De inhoud van dat bestand zou het volgende moeten zijn:

        function init() {
          var structure = struct({
            aChar : char(),
            anInt : int32(),
            bitFlag : bitfield("bool", 1),
            padding : bitfield("unsigned", 7),
          })
          return structure;
        }
        

De structuur die Okteta toont is altijd de "return value" van de init-functie.

U kunt de volgende functies aanroepen om een primitief type te creeëren:

  • char()

  • int8(), int16(), int32() or int64()

  • uint8(), uint16(), uint32() or uint64()

  • bool8(), bool16(), bool32() or bool64()

  • float()

  • double()

De bitfield-functie heeft twee parameters nodig, de eerste is een tekenreeks bestaande uit bool, signed of unsigned. De tweede parameter is een integer die de breedte in bits instelt.

Complexere structuren

Vervolgens creëren we een definitie voor een complexere structuur waaraan we de naam "complex" geven en we slaan dit op in een bestand genaamd complex.osd. Deze structuur bestaat uit twee arrays (een met een vaste lengte en een waarvan de lengte tijdens de uitvoering wordt bepaald), een geneste structuur en een verzameling.


          <?xml version="1.0" encoding="UTF-8"?>
          <data>
            <struct name="complex">
              <primitive name="size" type="UInt8" />
              <union name="aUnion">
                <array name="fourBytes" length="4">
                  <primitive type="Int8" />
                </array>
              </union>
              <struct name="nested">
                <array name="string" length="size"> <!-- verwijzing naar de field-size boven -->
                  <primitive type="Char" />
                </array>
              </struct>
            </struct>
          </data>
          

Dit zou overeen moeten komen met het volgende in pseudo-C/C++

struct complex {
            uint8_t size;
            union aUnion {
              int8_t fourBytes[4];
            };
            struct nested {
              char string[size] //geen geldige C++, refereert waarde van de grootte uint8
            };
          };
          

Opmerking

U kunt uiteraard de referentie-velden voor arrays met dynamische lengte alleen vóór het array hebben,

Vervolgens maken we het bestand complex.desktop net als in het vorige voorbeeld (ga na dat u X-KDE-PluginInfo-Name juist instelt) en ook hetzelfde doet om beide bestanden te installeren.

Meer informatie

Enkele voorbeelden voor structuurdefinities kunt u vinden in deze Git repository. Inclusief voorbeelden voor de bestand-header van PNG-bestanden en de ELF bestand-header. Een XML schema voor de structuur van een .osd-bestand kunt u hier vinden. Als u meer informatie wilt dan kunt u met mij contact opnemen via