{"id":2396,"date":"2019-12-14T22:26:27","date_gmt":"2019-12-14T19:26:27","guid":{"rendered":"http:\/\/demensdeum.com\/blog\/?p=2396"},"modified":"2024-12-16T22:32:32","modified_gmt":"2024-12-16T19:32:32","slug":"%d1%80%d0%b0%d0%b7%d1%80%d0%b0%d0%b1%d0%be%d1%82%d0%ba%d0%b0-%d0%b8%d0%b3%d1%80%d1%8b-%d0%b4%d0%bb%d1%8f-zx-spectrum-%d0%bd%d0%b0-c","status":"publish","type":"post","link":"https:\/\/demensdeum.com\/blog\/de\/2019\/12\/14\/%d1%80%d0%b0%d0%b7%d1%80%d0%b0%d0%b1%d0%be%d1%82%d0%ba%d0%b0-%d0%b8%d0%b3%d1%80%d1%8b-%d0%b4%d0%bb%d1%8f-zx-spectrum-%d0%bd%d0%b0-c\/","title":{"rendered":"Spieleentwicklung f\u00fcr ZX Spectrum in C"},"content":{"rendered":"<p>Dieser Nonsens-Beitrag ist der Entwicklung eines Spiels f\u00fcr den alten ZX Spectrum-Computer in C gewidmet. Werfen wir einen Blick auf den h\u00fcbschen Kerl:<\/p>\n<p><a href=\"https:\/\/www.flickr.com\/photos\/quenerapu\/2622099393\" target=\"_blank\" rel=\"noopener noreferrer\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-2398\" src=\"https:\/\/demensdeum.com\/blog\/wp-content\/uploads\/2019\/12\/2622099393_8199976c15_w.jpg\" alt=\"\" width=\"400\" height=\"294\" srcset=\"https:\/\/demensdeum.com\/blog\/wp-content\/uploads\/2019\/12\/2622099393_8199976c15_w.jpg 400w, https:\/\/demensdeum.com\/blog\/wp-content\/uploads\/2019\/12\/2622099393_8199976c15_w-300x221.jpg 300w\" sizes=\"auto, (max-width: 400px) 100vw, 400px\" \/><\/a><\/p>\n<p>Die Produktion begann 1982 und wurde bis 1992 produziert. Technische Eigenschaften der Maschine: 8-Bit-Z80-Prozessor, 16-128 KB Speicher und weitere Erweiterungen, wie der <b>AY-3-8910-Soundchip.<\/b><\/p>\n<p>Im Rahmen des Wettbewerbs <a href=\"http:\/\/rgb.yandex\/\" target=\"_blank\" rel=\"noopener noreferrer\">Yandex Retro Games Battle 2019<\/a> f\u00fcr diese Maschine habe ich ein Spiel geschrieben namens Interceptor 2020. Da ich keine Zeit hatte, die Assemblersprache f\u00fcr den Z80 zu lernen, beschloss ich, sie in C zu entwickeln. Als Toolchain habe ich ein fertiges Set gew\u00e4hlt &#8211; z88dk, das C-Compiler und viele Hilfsbibliotheken enth\u00e4lt, um die Implementierung von Anwendungen f\u00fcr das Spectrum zu beschleunigen. Es unterst\u00fctzt auch viele andere Z80-Ger\u00e4te, wie z. B. Taschenrechner von MSX und Texas Instruments.<\/p>\n<p>Als n\u00e4chstes beschreibe ich meinen oberfl\u00e4chlichen Flug \u00fcber die Computerarchitektur, die z88dk-Toolchain, und zeige, wie ich es geschafft habe, den OOP-Ansatz zu implementieren und Designmuster zu verwenden.<\/p>\n<h3>Installationsfunktionen<\/h3>\n<p>Die Installation von z88dk sollte gem\u00e4\u00df der Anleitung aus dem Repository erfolgen, f\u00fcr Ubuntu-Benutzer m\u00f6chte ich jedoch eine Funktion anmerken &#8211; Wenn Sie bereits Compiler f\u00fcr Z80 aus Deb-Paketen installiert haben, sollten Sie diese entfernen, da z88dk aufgrund der Inkompatibilit\u00e4t der Toolchain-Compiler-Versionen standardm\u00e4\u00dfig auf sie zugreift.< \/p><\/p>\n<h3>Hallo Welt<\/h3>\n<p>Hello World zu schreiben ist sehr einfach:<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-unknown\" data-lang=\"unknown\"><code>#include \n\nvoid main()\n{\n    printf(\"Hello World\");\n}\n<\/code><\/pre>\n<\/div>\n<p>Es ist noch einfacher, eine Tap-Datei zu kompilieren:<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-unknown\" data-lang=\"unknown\"><code>zcc +zx -lndos -create-app -o helloworld helloworld.c\n<\/code><\/pre>\n<\/div>\n<p>Verwenden Sie zum Ausf\u00fchren einen beliebigen ZX Spectrum-Emulator mit Tap-Datei-Unterst\u00fctzung, zum Beispiel online:<br \/><a href=\"http:\/\/jsspeccy.zxdemo.org\/\">http:\/\/jsspeccy.zxdemo.org\/<\/a><\/p>\n<h3>Zeichnen Sie im Vollbildmodus auf das Bild<\/h3>\n<p><i>tl;dr Bilder werden in Kacheln gezeichnet, Kacheln mit einer Gr\u00f6\u00dfe von 8&#215;8 Pixeln, die Kacheln selbst sind in die Schriftart Spectrum eingebaut, dann wird das Bild als Linie aus den Indizes gedruckt. <\/i><\/p>\n<p>Die Sprite- und Kachelausgabebibliothek SP1 gibt Kacheln mithilfe von UDG aus. Das Bild wird in eine Reihe einzelner UDGs (Kacheln) \u00fcbersetzt und dann mithilfe von Indizes auf dem Bildschirm zusammengesetzt. Beachten Sie, dass UDG zum Anzeigen von Text verwendet wird. Wenn Ihr Bild einen sehr gro\u00dfen Satz von Kacheln enth\u00e4lt (z. B. mehr als 128 Kacheln), m\u00fcssen Sie \u00fcber die Grenzen des Satzes hinausgehen und das Standardspektrum l\u00f6schen Schriftart. Um diese Einschr\u00e4nkung zu umgehen, habe ich eine Basis von 128 &#8211; 255 durch Vereinfachung der Bilder, wobei die urspr\u00fcngliche Schriftart erhalten bleibt. \u00dcber die Vereinfachung der Bilder unten.<\/p>\n<p>Um Vollbildbilder zu zeichnen, m\u00fcssen Sie sich mit drei Dienstprogrammen ausr\u00fcsten:<br \/>Gimp<br \/>img2spec<br \/>png2c-z88dk<\/p>\n<p>F\u00fcr echte ZX-M\u00e4nner, echte Retro-Krieger gibt es eine M\u00f6glichkeit: \u00d6ffnen Sie einen Grafikeditor mit der Spectrum-Palette, kennen Sie die Funktionen der Bildausgabe, bereiten Sie sie manuell vor und laden Sie sie mit png2c-z88dk oder png2scr hoch.< \/p><\/p>\n<p>Der einfachere Weg &#8211; Nehmen Sie ein 32-Bit-Bild, \u00e4ndern Sie die Anzahl der Farben in Gimp auf 3-4, bearbeiten Sie es leicht und importieren Sie es dann in img2spec, um nicht manuell mit Farbbeschr\u00e4nkungen zu arbeiten. Exportieren Sie PNG und konvertieren Sie es mit PNG2C in ein C-Array. z88dk.<\/p >\n<p>Sie sollten bedenken, dass f\u00fcr einen erfolgreichen Export jede Kachel nicht mehr als zwei Farben enthalten darf.<\/p>\n<p>Als Ergebnis erhalten Sie eine h-Datei, die die Anzahl der eindeutigen Kacheln enth\u00e4lt. Wenn es mehr als ~128 sind, vereinfachen Sie das Bild in Gimp (erh\u00f6hen Sie die Wiederholbarkeit) und f\u00fchren Sie den Exportvorgang \u00fcber ein neues aus .<\/p>\n<p>Nach dem Export laden Sie buchst\u00e4blich die \u201eSchriftart\u201c aus den Kacheln und drucken den \u201eText\u201c aus den Kachelindizes auf den Bildschirm. Unten ist ein Beispiel aus der Render-\u201eKlasse\u201c:<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-unknown\" data-lang=\"unknown\"><code>\/\/ \u0433\u0440\u0443\u0437\u0438\u043c \u0448\u0440\u0438\u0444\u0442 \u0432 \u043f\u0430\u043c\u044f\u0442\u044c\n    unsigned char *pt = fullscreenImage->tiles;\n\n    for (i = 0; i < fullscreenImage->tilesLength; i++, pt += 8) {\n            sp1_TileEntry(fullscreenImage->tilesBase + i, pt);\n    }\n\n    \/\/ \u0441\u0442\u0430\u0432\u0438\u043c \u043a\u0443\u0440\u0441\u043e\u0440 \u0432 0,0\n    sp1_SetPrintPos(&ps0, 0, 0);\n\n    \/\/ \u043f\u0435\u0447\u0430\u0442\u0430\u0435\u043c \u0441\u0442\u0440\u043e\u043a\u0443\n    sp1_PrintString(&ps0, fullscreenImage->ptiles);\n<\/code><\/pre>\n<\/div>\n<h3>Sprites auf dem Bildschirm zeichnen<\/h3>\n<p>Als n\u00e4chstes beschreibe ich eine Methode zum Zeichnen von Sprites mit 16 x 16 Pixeln auf dem Bildschirm. Ich bin nicht zur Animation und zum \u00c4ndern der Farben gekommen, weil&#8230; Es ist trivial, dass mir schon zu diesem Zeitpunkt, wie ich annehme, der Speicher ausgegangen ist. Daher enth\u00e4lt das Spiel nur transparente monochrome Sprites.<\/p>\n<p>Wir zeichnen ein monochromes PNG-Bild 16&#215;16 in Gimp, dann \u00fcbersetzen wir es mit png2sp1sprite in eine ASM-Assembly-Datei, im C-Code deklarieren wir Arrays aus der Assembly-Datei und f\u00fcgen die Datei in der Assembly-Phase hinzu.< \/p><\/p>\n<p>Nach der Deklaration der Sprite-Ressource muss diese an der gew\u00fcnschten Position zum Bildschirm hinzugef\u00fcgt werden. Nachfolgend finden Sie ein Beispiel f\u00fcr den Code f\u00fcr die \u201eKlasse\u201c des Spielobjekts:<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-unknown\" data-lang=\"unknown\"><code>    struct sp1_ss *bubble_sprite = sp1_CreateSpr(SP1_DRAW_MASK2LB, SP1_TYPE_2BYTE, 3, 0, 0);\n    sp1_AddColSpr(bubble_sprite, SP1_DRAW_MASK2,    SP1_TYPE_2BYTE, col2-col1, 0);\n    sp1_AddColSpr(bubble_sprite, SP1_DRAW_MASK2RB,  SP1_TYPE_2BYTE, 0, 0);\n    sp1_IterateSprChar(bubble_sprite, initialiseColour);\n<\/code><\/pre>\n<\/div>\n<p>Anhand der Namen der Funktionen k\u00f6nnen Sie die Bedeutung von &#8211; Weisen Sie dem Sprite Speicher zu, f\u00fcgen Sie zwei 8&#215;8 Spalten hinzu und f\u00fcgen Sie eine Farbe f\u00fcr das Sprite hinzu.<\/p>\n<p>Die Position des Sprites wird in jedem Frame angezeigt:<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-unknown\" data-lang=\"unknown\"><code>sp1_MoveSprPix(gameObject->gameObjectSprite, Renderer_fullScreenRect, gameObject->sprite_col, gameObject->x, gameObject->y);\n<\/code><\/pre>\n<\/div>\n<h3>OOP emulieren<\/h3>\n<p>In C gibt es keine Syntax f\u00fcr OOP. Was sollten Sie tun, wenn Sie es trotzdem wirklich wollen? Sie m\u00fcssen Ihren Geist verbinden und sich dar\u00fcber im Klaren sein, dass es so etwas wie OOP-Ausr\u00fcstung nicht gibt; letztendlich kommt es auf eine der Maschinenarchitekturen an, in denen es einfach kein Konzept eines Objekts und andere damit verbundene Abstraktionen gibt.< \/p><\/p>\n<p>Diese Tatsache hat mich sehr lange daran gehindert zu verstehen, warum OOP \u00fcberhaupt ben\u00f6tigt wird, warum es notwendig ist, es zu verwenden, wenn am Ende alles auf Maschinencode hinausl\u00e4uft.<\/p>\n<p>Nachdem ich jedoch in der Produktentwicklung gearbeitet hatte, entdeckte ich die Vorz\u00fcge dieses Programmierparadigmas, vor allem nat\u00fcrlich die Entwicklungsflexibilit\u00e4t, Code-Schutzmechanismen mit dem richtigen Ansatz, die Reduzierung der Entropie und die Vereinfachung der Teamarbeit. Alle oben genannten Vorteile basieren auf drei S\u00e4ulen: Polymorphismus, Kapselung, Vererbung.<\/p>\n<p>Bemerkenswert ist auch die Vereinfachung der L\u00f6sung von Problemen im Zusammenhang mit der Anwendungsarchitektur, da 80 % der Architekturprobleme im letzten Jahrhundert von Informatikern gel\u00f6st und in der Literatur zu Entwurfsmustern beschrieben wurden. Als N\u00e4chstes beschreibe ich M\u00f6glichkeiten, C eine OOP-\u00e4hnliche Syntax hinzuzuf\u00fcgen.<\/p>\n<p>Es ist bequemer, C-Strukturen als Grundlage f\u00fcr die Speicherung von Daten einer Klasseninstanz zu verwenden. Nat\u00fcrlich k\u00f6nnen Sie einen Bytepuffer verwenden und Ihre eigene Struktur f\u00fcr Klassen und Methoden erstellen, aber warum das Rad neu erfinden? Schlie\u00dflich erfinden wir die Syntax bereits neu.<\/p>\n<h4>Klassendaten<\/h4>\n<p>Beispiel f\u00fcr GameObject-\u201eKlassen\u201c-Datenfelder:<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-unknown\" data-lang=\"unknown\"><code>struct GameObjectStruct {\n    struct sp1_ss *gameObjectSprite;\n    unsigned char *sprite_col;\n    unsigned char x;\n    unsigned char y;\n    unsigned char referenceCount;\n    unsigned char beforeHideX;\n    unsigned char beforeHideY;\n};\ntypedef struct GameObjectStruct GameObject;\n<\/code><\/pre>\n<\/div>\n<p>Speichern Sie unsere Klasse als \u201eGameObject.h\u201c, f\u00fcgen Sie \u201eGameObject.h\u201c an der richtigen Stelle ein und verwenden Sie sie.<\/p>\n<h4>Klassenmethoden<\/h4>\n<p>Unter Ber\u00fccksichtigung der Erfahrung der Entwickler der Objective-C-Sprache besteht die Signatur einer Klassenmethode aus Funktionen in einem globalen Bereich. Das erste Argument ist immer die Datenstruktur, gefolgt von den Methodenargumenten. Unten finden Sie ein Beispiel f\u00fcr eine \u201eMethode\u201c der \u201eKlasse\u201c GameObject:<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-unknown\" data-lang=\"unknown\"><code>void GameObject_hide(GameObject *gameObject) {\n    gameObject->beforeHideX = gameObject->x;\n    gameObject->beforeHideY = gameObject->y;\n    gameObject->y = 200;\n}\n<\/code><\/pre>\n<\/div>\n<p>Der Methodenaufruf sieht so aus:<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-unknown\" data-lang=\"unknown\"><code>GameObject_hide(gameObject);\n<\/code><\/pre>\n<\/div>\n<p>Konstruktoren und Destruktoren werden auf die gleiche Weise implementiert. Es ist m\u00f6glich, einen Konstruktor als Allokator und Feldinitialisierer zu implementieren, aber ich bevorzuge es, die Objektzuweisung und -initialisierung separat zu steuern.<\/p>\n<h4>Mit dem Ged\u00e4chtnis arbeiten<\/h4>\n<p>Manuelle Speicherverwaltung des Formulars mit malloc und frei in neue und gel\u00f6schte Makros eingebunden, passend zu C++:<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-unknown\" data-lang=\"unknown\"><code>#define new(X) (X*)malloc(sizeof(X))\n#define delete(X) free(X)\n<\/code><\/pre>\n<\/div>\n<p>F\u00fcr Objekte, die von mehreren Klassen gleichzeitig verwendet werden, wird eine halbmanuelle Speicherverwaltung basierend auf Referenzz\u00e4hlung implementiert, nach dem Vorbild und der \u00c4hnlichkeit des alten Objective-C Runtime ARC-Mechanismus:<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-unknown\" data-lang=\"unknown\"><code>void GameObject_retain(GameObject *gameObject) {\n    gameObject->referenceCount++;\n}\n\nvoid GameObject_release(GameObject *gameObject) {\n    gameObject->referenceCount--;\n\n    if (gameObject->referenceCount < 1) { sp1_MoveSprAbs(gameObject->gameObjectSprite, &Renderer_fullScreenRect, NULL, 0, 34, 0, 0);\n        sp1_DeleteSpr(gameObject->gameObjectSprite);\n        delete(gameObject);\n    }\n}\n<\/code><\/pre>\n<\/div>\n<p>Daher muss jede Klasse die Verwendung eines gemeinsam genutzten Objekts mithilfe von \u201eRetain\u201c deklarieren und das Eigentum durch \u201eRelease\u201c freigeben. Die moderne Version von ARC verwendet automatische Aufbewahrungs-\/Freigabeaufrufe.<\/p>\n<h3>Ton!<\/h3>\n<p>Der Spectrum verf\u00fcgt \u00fcber einen Hocht\u00f6ner, der 1-Bit-Musik wiedergeben kann; damalige Komponisten konnten bis zu 4 Tonkan\u00e4le gleichzeitig wiedergeben.<\/p>\n<p>Spectrum 128k enth\u00e4lt einen separaten Soundchip AY-3-8910, der Tracker-Musik abspielen kann.<\/p>\n<p>F\u00fcr die Nutzung des Hocht\u00f6ners im z88dk wird eine Bibliothek angeboten<\/p>\n<h3>Was noch gelernt werden muss<\/h3>\n<p>Ich war daran interessiert, das Spectrum kennenzulernen, das Spiel mit dem z88dk zu implementieren und viele interessante Dinge zu lernen. Ich muss noch viel lernen, zum Beispiel den Z80-Assembler, da er mir erm\u00f6glicht, die volle Leistung des Spectrum zu nutzen, mit Speicherb\u00e4nken zu arbeiten und mit dem Soundchip AY-3-8910 zu arbeiten. Ich hoffe, n\u00e4chstes Jahr am Wettbewerb teilnehmen zu k\u00f6nnen!<\/p>\n<h3>Links<\/h3>\n<p><a href=\"https:\/\/rgb.yandex\/\" target=\"_blank\" rel=\"noopener noreferrer\">https:\/\/rgb.yandex<\/a><br \/><a href=\"https:\/\/vk.com\/sinc_lair\" target=\"_blank\" rel=\"noopener noreferrer\">https:\/\/vk.com\/sinc_lair<\/a><br \/>\n<a href=\"https:\/\/www.z88dk.org\/forum\/\" target=\"_blank\" rel=\"noopener noreferrer\">https:\/\/www.z88dk.org\/forum\/<\/a><\/p>\n<h3>Quellcode<\/h3>\n<p><a href=\"https:\/\/gitlab.com\/demensdeum\/zx-projects\/tree\/master\/interceptor2020\" target=\"_blank\" rel=\"noopener noreferrer\">https:\/\/gitlab.com\/demensdeum\/ zx-projects\/tree\/master\/interceptor2020<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Dieser Nonsens-Beitrag ist der Entwicklung eines Spiels f\u00fcr den alten ZX Spectrum-Computer in C gewidmet. Werfen wir einen Blick auf den h\u00fcbschen Kerl: Die Produktion begann 1982 und wurde bis 1992 produziert. Technische Eigenschaften der Maschine: 8-Bit-Z80-Prozessor, 16-128 KB Speicher und weitere Erweiterungen, wie der AY-3-8910-Soundchip. Im Rahmen des Wettbewerbs Yandex Retro Games Battle 2019<a class=\"more-link\" href=\"https:\/\/demensdeum.com\/blog\/de\/2019\/12\/14\/%d1%80%d0%b0%d0%b7%d1%80%d0%b0%d0%b1%d0%be%d1%82%d0%ba%d0%b0-%d0%b8%d0%b3%d1%80%d1%8b-%d0%b4%d0%bb%d1%8f-zx-spectrum-%d0%bd%d0%b0-c\/\">Continue reading <span class=\"screen-reader-text\">&#8220;Spieleentwicklung f\u00fcr ZX Spectrum in C&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[49,81,61],"tags":[134,133],"class_list":["post-2396","post","type-post","status-publish","format-standard","hentry","category-blog","category-demos","category-techie","tag-spectrum","tag-z88dk","entry"],"translation":{"provider":"WPGlobus","version":"3.0.2","language":"de","enabled_languages":["en","ru","zh","de","fr","ja","pt","hi"],"languages":{"en":{"title":true,"content":true,"excerpt":false},"ru":{"title":true,"content":true,"excerpt":false},"zh":{"title":true,"content":true,"excerpt":false},"de":{"title":true,"content":true,"excerpt":false},"fr":{"title":true,"content":true,"excerpt":false},"ja":{"title":true,"content":true,"excerpt":false},"pt":{"title":true,"content":true,"excerpt":false},"hi":{"title":false,"content":false,"excerpt":false}}},"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/demensdeum.com\/blog\/de\/wp-json\/wp\/v2\/posts\/2396","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/demensdeum.com\/blog\/de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/demensdeum.com\/blog\/de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/demensdeum.com\/blog\/de\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/demensdeum.com\/blog\/de\/wp-json\/wp\/v2\/comments?post=2396"}],"version-history":[{"count":32,"href":"https:\/\/demensdeum.com\/blog\/de\/wp-json\/wp\/v2\/posts\/2396\/revisions"}],"predecessor-version":[{"id":3930,"href":"https:\/\/demensdeum.com\/blog\/de\/wp-json\/wp\/v2\/posts\/2396\/revisions\/3930"}],"wp:attachment":[{"href":"https:\/\/demensdeum.com\/blog\/de\/wp-json\/wp\/v2\/media?parent=2396"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/demensdeum.com\/blog\/de\/wp-json\/wp\/v2\/categories?post=2396"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/demensdeum.com\/blog\/de\/wp-json\/wp\/v2\/tags?post=2396"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}