Herramienta de estructuras

General

La herramienta de estructuras permite el análisis y la edición de arrays de bytes basados en definiciones de estructuras creadas por el usuario, que se pueden construir a partir de arrays, uniones, tipos primitivos y valores de enumeración.

Posee un diálogo de ajustes propio, que se puede mostrar usando el botón Ajustes. Existen varias opciones que se pueden configurar, como el estilo (decimal, hexadecimal o binario) en el que se muestran los valores. También es posible escoger qué definiciones de estructuras se cargan y qué estructuras se muestran en el visor.

Las estructuras se definen en archivos de definición de estructuras de Okteta (basados en XML, con la extensión .osd). Adicionalmente, un archivo .desktop contiene metadatos sobre dicho archivo de descripción de estructura, como el autor, su página web y la licencia de uso.

En la actualidad no se pueden crear ni editar definiciones de estructuras desde dentro del programa, por lo que estas operaciones se deben realizar de forma manual como se describe en las siguientes secciones.

Instalación de definiciones de estructuras

Instalación usando KNewStuff

El modo más fácil para instalar nuevas definiciones de estructuras consiste en usar la utilidad KNewStuff integrada en Okteta. Para instalar una estructura existente, abra el diálogo de preferencias de la herramienta «Estructuras». Seleccione ahí la pestaña Gestión de estructuras y pulse el botón Obtener nuevas estructuras.... El diálogo que se muestra a continuación le permite instalar y desinstalar estructuras.

Instalación manual de definiciones de estructuras

La herramienta de estructuras comprueba la existencia de descripciones de estructuras en el subdirectorio okteta/structures/ del directorio del usuario para los datos de programas (puede encontrarlo ejecutando qtpaths --paths GenericDataLocation). Es posible que tenga que crear este directorio si aún no ha instalado ninguna definición de estructura.

Existen dos archivos para cada definición de estructura: uno para la definición y otro con la extensión .desktop para los metadatos (autor, versión, etc.).

En este directorio existe un subdirectorio para cada definición de estructura, que contiene tanto el archivo .desktop como el archivo .osd o el archivo main.js de la definición.

Por ejemplo, con el directorio de datos de programas qtpaths --paths GenericDataLocation y una definición de estructura llamada «EstructuraEjemplo», existirá el directorio okteta/structures/EstructuraEjemplo, que contendrá los archivos EstructuraEjemplo.desktop y EstructuraEjemplo.osd.

Uso de las estructuras recién instaladas

Si ha instalado una nueva definición de estructura, tendrá que reiniciar Okteta antes de poder usarla. Tras iniciar Okteta, abra el diálogo de preferencias de la herramienta de estructuras. Seleccione allí la pestaña Gestión de estructuras y asegúrese de marcar la definición de estructura en cuestión. Cambie luego a la pestaña Estructuras y asegúrese de que el elemento en cuestión aparece listado en la parte de la derecha.

Compartir definiciones de estructuras

No necesita crear una definición para las estructuras comunes, pero sí puede reutilizar alguna definición existente de sitios como store.kde.org.

También es posible que quiera compartir una definición. Para ello, cree un archivo comprimido (por ejemplo, un archivo comprimido tar, .tar.gz) que contenga únicamente el subdirectorio con el archivo .desktop y el archivo de definición de la estructura. Según el ejemplo de la sección anterior, sería el directorio EstructuraEjemplo con todo su contenido. El uso de este formato para compartir las definiciones de estructuras permite instalarlas en Okteta sin tener que hacerlo manualmente.

Creación de definiciones de estructuras

Nota

Puede encontrar una guía más actualizada (aunque incompleta) para escribir definiciones de estructuras en la Wiki de KDE UserBase.

Existen dos modos diferentes de crear definiciones de estructuras. El primero consiste en escritor la definición en XML, mientras que el otro usa JavaScript. La aproximación en JavaScript le permite crear estructuras más complejas con funcionalidades como, por ejemplo, la de validar la estructura. El uso de XML le proporciona menos funcionalidades, aunque el más sencillo si lo único que necesita es una estructura estática. Si lo que necesita es una estructura dinámica (es decir, en la que la longitud de los arrays depende de otros valores de la estructura, o el esquema de la estructura es diferente cuando cambian algunos valores miembro), tendrá que escribir la definición de la estructura en JavaScript. Existe una excepción a esta regla: si tiene un array cuya longitud se supone que es exactamente la misma que otro valor de la estructura, podrá usar XML. Pero si es algo como longitud - 1, debe usar JavaScript.

Formato de archivo XML de definición de estructuras

Nota

Puede encontrar una guía más actualizada (aunque incompleta) para escribir definiciones de estructuras en la Wiki de KDE UserBase.

El archivo XML con la extensión .osd tiene un elemento raíz: <data>, que no posee atributos. Dentro de este elemento debe existir uno de los siguientes elementos:

<primitive>

Para crear tipos de datos de primitivas como, por ejemplo, int y float. Este elemento no acepta subelementos y puede tener los siguientes atributos:

type

El tipo de esta primitiva. Debe ser uno de los siguientes:

  • char para un carácter ASCII de 8 bits

  • int8, int16, int32, int64 para enteros con signo del tamaño indicado

  • uint8, uint16, uint32, uint64 para enteros sin signo del tamaño indicado

  • bool8, bool16, bool32, bool64 para booleanos sin signo (0 = falso, cualquier otro valor = cierto) del tamaño indicado

  • float para números en coma flotante IEEE754 de 32 bits

  • double para números en coma flotante IEEE754 de 64 bits

<bitfield>

Para crear un campo de bits. Este elemento no acepta subelementos y puede tener los siguientes atributos:

width

El número de bits que usa este campo de bits. Debe estar comprendido entre 1 y 64.

type

El tipo de este campo de bits. Debe ser uno de los siguientes:

  • unsigned para un campo de bits donde el valor se interpretará como valor sin signo (comprendido entre 0 y 2width - 1)

  • signed para un campo de bits donde el valor se interpretará como valor con signo (comprendido entre -2width - 1 y 2width - 1 - 1)

  • bool para un campo de bits donde el valor se interpretará como booleano

Nota

Recuerde añadir siempre un relleno tras un <bitfield>, ya que, de lo contrario, el siguiente elemento comenzará en medio de un byte (excepto con cadenas y arrays, que añaden relleno de forma automática). Por supuesto, el relleno no será necesario si quiere este comportamiento.

<enum>

Para crear un tipo de primitiva, excepto donde los valores se muestran como miembros de una enumeración, si es posible. Este elemento no acepta ningún subelemento (en caso contrario necesitaría una etiqueta <enumDef> en el archivo para hacer referencia a él). Posee los siguientes atributos:

enum

El enumerador subyacente para este valor. Debe concordar con el atributo name de una de las etiquetas <enumDef> de este archivo.

type

El tipo de este enumerador. Consulte el atributo tipo de <primitive>. La única diferencia es que Double y Float no tienen sentido.

<flags>

Esto es lo mismo que <enum> con la única diferencia de que los valores se representan como un bitwise-or de todos los valores de la enumeración.

<struct>

Para crear una estructura. El resto de elementos (incluido <struct>) pueden ser hijos de este, en cuyo caso serán parte de la estructura resultante.

<union>

Para crear una unión. En esencia es lo miso que <struct>, excepto por el hecho de que todos los elementos hijos comenzarán desde el mismo desplazamiento. Resulta útil para interpretar la misma secuencia de bytes de diferentes modos.

<array>

Para crear un array. Este elemento acepta únicamente un hijo (el tipo subyacente del array), que puede ser cualquiera de los elementos, incluso el mismo <array>. También posee los siguientes atributos:

length

El número de elementos del array como número decimal. De forma alternativa, también puede ser una cadena que coincida con el atributo de nombre de un elemento <primitive>, <enum> o <flags> previamente definido. En este caso, la longitud será el valor de dicho elemento. En la actualidad está limitado a 10000, ya que un array mayor usaría demasiada memoria y haría que el programa fuese muy lento.

<string>

Para crear una cadena con diferentes codificaciones. Por omisión, obtendrá una cadena de estilo C terminada con NULL. No obstante, se pueden crear distintos tipos de cadenas con los siguientes atributos:

terminatedBy

Este atributo determina con qué código unicode termina la cadena. Debe ser un número hexadecimal (que puede comenzar por 0x). Cuando se usa la codificación ASCII, solo tienen sentido los valores inferiores a 0x7f, incluido. Si no se indica este valor ni maxCharCount ni maxByteCount, se asume que es 0 (cadena de estilo C)

maxCharCount

El número máximo de caracteres que puede contener la cadena. Si también se especifica terminatedBy, la cadena se terminará con cualquiera de los dos que se alcance primero. Esto es mutuamente exclusivo con maxByteCount

maxByteCount

El número máximo de bytes que esta cadena puede tener de longitud. Si también se especifica terminatedBy, la cadena se terminará con cualquiera de las dos que se alcance primero. Esto es mutuamente exclusivo con maxCharCount. Con codificaciones como ASCII es idéntico a maxCharCount.

type

La codificación de la cadena. Puede ser una de las siguientes:

  • ASCII

  • LATIN-1

  • UTF-8

  • UTF-16-LE o UTF-16-BE. Si no se indica uno de los sufijos -LE o -BE, se asume «little endian».

  • UTF-32-LE o UTF-32-BE. Si no se indica uno de los sufijos -LE o -BE, se asume «little endian».

Todos los elementos aceptan también el atributo name, que es visible en las vistas de las estructuras.

Un ejemplo de definición de estructura en XML y en JavaScript

Nota

Puede encontrar una guía más actualizada (aunque incompleta) para escribir definiciones de estructuras en la Wiki de KDE UserBase.

El paso en común compartido por ambas aproximaciones

Nuestro archivo de metadatos sería semejante al siguiente:

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

          Name=Estructura de prueba simple
          Comment=Una estructura de prueba muy sencilla que solo contiene dos elementos

          X-KDE-PluginInfo-Author=Alex Richardson
          X-KDE-PluginInfo-Email=mi.direccion@email.org
          X-KDE-PluginInfo-Name=estructurasimple
          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
          

El icono que muestra Okteta para esta estructura puede ser cualquier cosa que encuentre al ejecutar kdialog --geticon o una ruta a un icono

Estos campos deberían resultar autoexplicativos, excepto X-KDE-PluginInfo-Name. El valor de este campo debe tener el nombre del directorio que contiene el archivo, así como el nombre del archivo .desktop. Al crear las definiciones de una estructura XML, el nombre del archivo .osd debe coincidir con el nombre.

En este ejemplo tendremos un directorio llamado estructurasimple que contendrá el archivo estructurasimple.desktop. Al definir estructuras en XML, el directorio también contendrá un archivo llamado estructurasimple.osd. Si usáramos JavaScript tendremos en su lugar un archivo llamado main.js.

Una definición de estructura sencilla en XML

Para comenzar vamos a crear una definición para una estructura de pruebas muy simple que solo contiene tipos de datos enteros (un carácter, un entero de 32 bits y un campo de bits). Esto se expresaría en C/C++ del siguiente modo:

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

El primer paso es escribir el archivo .osd según el formato de archivo descrito en la sección anterior. Lo llamaremos estructurasencilla.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>
          

que es bastante similar a la definición de C/C++.

Ahora debe crear un subdirectorio estructurasimple dentro del directorio de instalación de la estructura (consulte la instalación manual de definiciones de estructuras) y copiar los dos archivos en dicho directorio. Luego tendrá que reiniciar Okteta para poder usar la nueva estructura.

La misma estructura en JavaScript

Para implementar la estructura anterior en JavaScript, cree un archivo llamado main.js en lugar de simplestruct.osd y cambie X-KDE-PluginInfo-Category=structure a X-KDE-PluginInfo-Category=structure/js. El contenido de este archivo debe ser:

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

La estructura mostrada por Okteta es siempre el valor devuelto por la función init.

Puede llamar a las siguientes funciones para crear un tipo de primitiva:

  • char()

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

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

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

  • float()

  • double()

La función «bitfield» tiene dos parámetros: el primero es una cadena que contiene bool, signed o unsigned; el segundo parámetro es un entero que establece el ancho en bits.

Estructuras más complejas

A continuación crearemos una definición de una estructura más compleja que llamaremos «compleja» y guardaremos en un archivo llamado compleja.osd. Esta estructura contendrá dos arrays (uno de longitud fija y otro donde la longitud se determina en tiempo de ejecución), así como una estructura anidada y una unión.


          <?xml version="1.0" encoding="UTF-8"?>
          <data>
            <struct name="compleja">
              <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"> <!-- hace referencia a la longitud del campo anterior -->
                  <primitive type="Char" />
                </array>
              </struct>
            </struct>
          </data>
          

Esto se correspondería con el siguiente pseudocódigo C/C++

struct complex {
            uint8_t size;
            union aUnion {
              int8_t fourBytes[4];
            };
            struct nested {
              char string[size] //no válido en C++, hace referencia a valor de tamaño uint8
            };
          };
          

Nota

Obviamente, solo puede tener campos de referencia a arrays de longitud dinámica antes del propio array.

A continuación creamos el archivo compleja.desktop del mismo modo que en el ejemplo anterior (asegúrese de fijar el valor X-KDE-PluginInfo-Name correctamente) y haga lo mismo para instalar ambos archivos.

Más información

Puede encontrar unas cuantas definiciones de estructuras de ejemplo en el repositorio Git. Encontrará, por ejemplo, las cabeceras de los archivos PNG y ELF. Puede encontrar un esquema XML que describe la estructura de los archivos .osd aquí. Si necesita más información, contacte conmigo en .