{"id":77,"date":"2009-07-06T18:51:36","date_gmt":"2009-07-06T18:51:36","guid":{"rendered":"https:\/\/blogs.scummvm.org\/upthorn\/?p=77"},"modified":"2022-05-25T18:54:26","modified_gmt":"2022-05-25T18:54:26","slug":"updated-api-reference","status":"publish","type":"post","link":"https:\/\/blogs.scummvm.org\/upthorn\/2009\/07\/06\/updated-api-reference\/","title":{"rendered":"Updated API reference"},"content":{"rendered":"<p>Note: the following is copied from the source of <a href=\"http:\/\/wiki.scummvm.org\/index.php\/API-Truecolor\" target=\"_blank\" rel=\"noopener\">the wiki reference article<\/a> I just wrote for it.<\/p>\n<h2><span class=\"mw-headline\"> Introduction <\/span><\/h2>\n<p>This page is meant as a reference for developers implementing the Truecolor API in their engine and backend modules. This page will provide a complete spec of API requirements and suggestions, as well as a protocol for engines and backends to follow during setup.<\/p>\n<p><strong>NOTE<\/strong>: This API was designed with backwards-compatibility for 8-bit Graphics only engines in mind. If your engine only uses 256 color graphics, you should not have to change anything, so long as the engine\u2019s ENABLE_RGB_COLOR setting matches the backend\u2019s during compilation, so that functions link properly.<\/p>\n<h2><span class=\"mw-headline\"> Truecolor API specifications <\/span><\/h2>\n<p><a name=\"Engine_specifications\"><\/a><\/p>\n<h3><span class=\"mw-headline\"> Engine specifications <\/span><\/h3>\n<ul>\n<li>Engines capable of some, but not all RGB formats <em><strong>must<\/strong><\/em> use Graphics::findCompatibleFormat with OSystem::getSupportedFormats() as the first parameter, and the engine\u2019s supported format list as the second parameter.<\/li>\n<li>Lists of formats supported by the engine <em><strong>must<\/strong><\/em> be in descending order of preference. That is, the first value is the most wanted, and the last value id the least wanted.<\/li>\n<li>Engines capable of any RGB format <em><strong>must<\/strong><\/em> use the first item in the backend\u2019s getSupportedFormats() list to generate graphics.<\/li>\n<li>Engines with static graphical resources <em>should<\/em> use the backend\u2019s preferred format, and convert resources upon loading, but are not required to.<\/li>\n<li>Engines which do not require the backend to handle alpha <em>should not<\/em> use XRBG1555 or XBGR1555 modes, but may do so.<\/li>\n<\/ul>\n<p><a name=\"Backend_specifications\"><\/a><\/p>\n<h3><span class=\"mw-headline\"> Backend specifications <\/span><\/h3>\n<ul>\n<li>When no format has been requested, or no game is running, the return value of getScreenFormat <em><strong>must<\/strong><\/em> equal that of Graphics::PixelFormat::createFormatCLUT8().<\/li>\n<li>If a requested format can not be set up, the backend <em><strong>must<\/strong><\/em> revert to 256 color mode (that is, Graphics::PixelFormat::createFormatCLUT8()).<\/li>\n<li>Backends <em><strong>must<\/strong><\/em> not change the return value of getScreenFormat outside of initBackend, initSize, or endGFXTransaction.<\/li>\n<li>Backends <em><strong>must<\/strong><\/em> be able to display 256 color cursors even when the screen format is set differently.<\/li>\n<li>Backends supporting GFX transactions <em><strong>must<\/strong><\/em> return kTransactionFormatNotSupported from endGFXTransaction when screen format change fails.<\/li>\n<li>Backends <em><strong>must<\/strong><\/em> place the highest color mode that is supported by the backend and the hardware at the beginning of the list returned by getSupportedFormats.<\/li>\n<li>Backends <em>should<\/em> support graphics in RGB(A) color order, even if their hardware uses a different color order, but are not required to.<\/li>\n<li>Backends supporting color order conversion with limited hardware may use Graphics::crossBlit, but are <strong>strongly recommended<\/strong> to use platform-optimized code.<\/li>\n<\/ul>\n<h2><span class=\"mw-headline\"> Truecolor API initialization protocol <\/span><\/h2>\n<p><a name=\"Engine_initialization_protocol\"><\/a><\/p>\n<h3><span class=\"mw-headline\"> Engine initialization protocol <\/span><\/h3>\n<p><strong>NOTE<\/strong>: This API was designed with backwards-compatibility for 8-bit Graphics only engines in mind. If your engine does not make use of per-pixel RGB color graphics, you should not have to change anything, so long as ENABLE_RGB_COLOR is set in configuration during compilation, so that functions link properly.<\/p>\n<ol>\n<li>Init with desired pixel format\n<ul>\n<li>If your engine can only produce graphics in one RGB color format, initialize a Graphics::PixelFormat to the desired format, and call initGraphics with a pointer to that format as the fourth parameter.\n<ul>\n<li>For instance, if your engine can only produce graphics in RGB555, you would say Graphics::PixelFormat myFormat(2, 3, 3, 3, 8, 10, 5, 0, 0);<\/li>\n<\/ul>\n<\/li>\n<li>If your engine can easily support any RGB mode (for instance if it converts from YUV), call initGraphics with NULL for the fourth parameter.<\/li>\n<li>If your engine can support more than one RGB mode, but not all of them\u2026\n<ol>\n<li>Produce a Common::List of Graphics::PixelFormat objects describing the supported formats. This list must be in order of descending preference, so that the most desired format is first, and the least desired is last.<\/li>\n<li>call initGraphics with this list of formats as the fourth parameter<\/li>\n<\/ol>\n<\/li>\n<\/ul>\n<\/li>\n<li>Check the return value of OSystem::getScreenFormat() to see if setup of your desired format was successful. If setup was not successful, it will return Graphics::PixelFormat::createFormatCLUT8();\n<ul>\n<li>If the setup was not successful, and your engine cannot run in 256 colors, display an error and return.<\/li>\n<li>Otherwise, initialize your engine to use the pixel format that getScreenFormat returned, and run normally.<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n<h4><span class=\"mw-headline\"> Example <\/span><\/h4>\n<p>Here is an example of a simple engine that uses the best color depth available to display and color-cycle this gradient:<\/p>\n<p><a href=\"https:\/\/blogs.scummvm.org\/upthorn\/wp-content\/uploads\/sites\/47\/2009\/07\/gradientrgb565.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-79\" src=\"https:\/\/blogs.scummvm.org\/upthorn\/wp-content\/uploads\/sites\/47\/2009\/07\/gradientrgb565.png\" alt=\"\" width=\"640\" height=\"480\" srcset=\"https:\/\/blogs.scummvm.org\/upthorn\/wp-content\/uploads\/sites\/47\/2009\/07\/gradientrgb565.png 640w, https:\/\/blogs.scummvm.org\/upthorn\/wp-content\/uploads\/sites\/47\/2009\/07\/gradientrgb565-300x225.png 300w\" sizes=\"auto, (max-width: 640px) 100vw, 640px\" \/><\/a><\/p>\n<pre>Common::Error QuuxEngine::run() {\r\n\tGraphics::PixelFormat ourFormat;\r\n\r\n\t\/\/ Request the backend to initialize a 640 x 480 surface with the best available format.\r\n\tinitGraphics(640, 480, true, NULL);\r\n\r\n\t\/\/ If our engine could only handle one format, we would specify it here instead of asking the backend:\r\n\t\/\/ \t\/\/ RGB555\r\n\t\/\/ \tourFormat(2, 3, 3, 3, 8, 10, 5, 0,  0);\r\n\t\/\/ \tinitGraphics(640, 480, true, &amp;ourFormat);\r\n\r\n\t\/\/ If our engine could handle only a few formats, this would look quite different:\r\n\t\/\/  \tCommon::List ourFormatList;\r\n\t\/\/\r\n\t\/\/ \t\/\/ RGB555\r\n\t\/\/ \tourFormat(2, 3, 3, 3, 8, 10, 5, 0,  0); \r\n\t\/\/ \tourFormatList.push_back(ourFormat);\r\n\r\n\t\/\/\r\n\t\/\/ \t\/\/ XRGB1555\r\n\t\/\/ \tourFormat(2, 3, 3, 3, 7, 10, 5, 0, 15); \r\n\t\/\/ \tourFormatList.push_back(ourFormat);\r\n\t\/\/ \t\r\n\t\/\/ \t\/\/ Use the best format which is compatible between our engine and the backend\r\n\r\n\t\/\/ \tinitGraphics(640, 480, true, ourFormatList);\r\n\r\n\t\/\/ Get the format the system was able to provide\r\n\t\/\/ in case it cannot support that format at our requested resolution\r\n\tourFormat = _system-&gt;getScreenFormat();\r\n\r\n \tbyte *offscreenBuffer = (byte *)malloc(640 * 480 * ourFormat.bytesPerPixel);\r\n\r\n \tif (ourFormat.bytesPerPixel == 1) {\r\n\t\t\/\/ Initialize palette to simulate RGB332\r\n\r\n\t\t\/\/ If our engine had no 256 color mode support, we would error out here:\r\n\t\t\/\/  \treturn Common::kUnsupportedColorMode;\r\n\r\n\t\tbyte palette[1024];\r\n\t\tmemset(&amp;palette,0,1024);\r\n\r\n\t\tbyte *dst = palette;\r\n\t\tfor (byte r = 0; r &lt; 8; r++) {\r\n\r\n\t\t\tfor (byte g = 0; g &lt; 8; g++) {\r\n\r\n\t\t\t\tfor (byte b = 0; b &lt; 4; b++) {\r\n\r\n\t\t\t\t\tdst[0] = r &lt;&lt; 5;\r\n\t\t\t\t\tdst[1] = g &lt;&lt; 5;\r\n\r\n\t\t\t\t\tdst[2] = b &lt;&lt; 6; dst[3] = 0; dst += 4; } } } _system-&gt;setPalette(palette,0,256);\r\n\t}\r\n\r\n\tuint32 t = 0;\r\n\r\n\t\/\/ Create a mask to limit the color from exceeding the bitdepth\r\n\t\/\/ The result is equivalent to:\r\n\t\/\/ \tuint32 mask = 0;\r\n\t\/\/ \tfor (int i = ourFormat.bytesPerPixel; i &gt; 0; i--) {\r\n\t\/\/ \t\tmask &lt;&lt;= 8;\r\n\t\/\/ \t\tmask |= 0xFF;\r\n\t\/\/ \t}\r\n\tuint32 mask = (1 &lt;&lt; (ourFormat.bytesPerPixel &lt;&lt; 3)) - 1;\r\n\r\n\t\/\/ Repeat this until the event manager tells us to stop\r\n\twhile (!shouldQuit()) {\r\n\r\n\t\t\/\/ Keep t from exceeding the number of bits in each pixel.\r\n\t\t\/\/ I think this is faster than \"t %= (ourFormat.bytesPerPixel * 8);\" would be.\r\n\t\tt &amp;= (ourFormat.bytesPerPixel &lt;&lt; 3) - 1;\r\n\r\n\t\t\/\/ Draw the actual gradient\r\n\t\tfor (int16 y = 0; y &lt; 480; y++) {\r\n\r\n\t\t\tuint8 *dst = offscreenBuffer + (y * 640 * ourFormat.bytesPerPixel);\r\n\r\n\t\t\tfor (int16 x = 0; x &lt; 640; x++) {\r\n\r\n\t\t\t\tuint32 color = (x * y) &amp; mask;\r\n\t\t\t\tcolor = (color &lt;&lt; t) | (color &gt;&gt; ((ourFormat.bytesPerPixel &lt;&lt; 3) - t));\r\n\r\n\t\t\t\t\/\/ Currently we have to jump through hoops to write variable-length data in an endian-safe manner.\r\n\t\t\t\t\/\/ In a real-life implementation, it would probably be better to have an if\/else-if tree or\r\n\t\t\t\t\/\/ a switch to determine the correct WRITE_UINT* function to use in the current bitdepth.\r\n\t\t\t\t\/\/ Though, something like this might end up being necessary for 24-bit pixels, anyway.\r\n\r\n#ifdef SCUMM_BIG_ENDIAN\r\n\t\t\t\tfor (int i = 0; i &lt; ourFormat.bytesPerPixel; i++) { dst[ourFormat.bytesPerPixel - i] = color &amp; 0xFF; color &gt;&gt;= 8;\r\n\t\t\t\t}\r\n\t\t\t\tdst += ourFormat.bytesPerPixel;\r\n\r\n#else\r\n\t\t\t\tfor (int i = ourFormat.bytesPerPixel; i &gt; 0; i--) {\r\n\r\n\t\t\t\t\t*dst++ = color &amp; 0xFF;\r\n\t\t\t\t\tcolor &gt;&gt;= 8;\r\n\r\n\t\t\t\t}\r\n#endif\r\n\t\t\t}\r\n\t\t}\r\n\t\t\/\/ Copy our gradient to the screen. The pitch of our image is the width * the number of bytes per pixel.\r\n\t\t_system-&gt;copyRectToScreen(offscreenBuffer, 640 * ourFormat.bytesPerPixel, 0, 0, 640, 480);\r\n\r\n\t\t\/\/ Tell the system to update the screen.\r\n\t\t_system-&gt;updateScreen();\r\n\r\n\t\t\/\/ Get new events from the event manager so the window doesn't appear non-responsive.\r\n\t\tparseEvents();\r\n\r\n\t\t\/\/ Wait a semi-arbitrary length in order to animate fluidly, but not insanely fast\r\n\t\t_system-&gt;delayMillis(66);\r\n\r\n\t\t\/\/ Increment our time variable, which doubles as our bit-shift counter.\r\n\t\tt++;\r\n\t}\r\n\treturn Common::kNoError;\r\n}\r\n<\/pre>\n<h3><span class=\"mw-headline\"> Backend initialization protocol <\/span><\/h3>\n<ol>\n<li>During first initialization, set the value that getScreenFormat returns to Graphics::PixelFormat::createFormatCLUT8()<\/li>\n<li>When initSize is called, attempt to set screen format with the PixelFormat pointed to by the format parameter\n<ul>\n<li>If format is NULL, use Graphics::PixelFormat::createFormatCLUT8()<\/li>\n<li>If requested screen format is supported, attempt to set screen up with it.\n<ul>\n<li>If setup is unsuccessful, fall back to previous color mode and set the value that getScreenFormat returns accordingly.\n<ul>\n<li>Note: During game initialization, this <strong>must always<\/strong> result in a fall-back to 256 color mode with getScreenFormat returning a value equivalent to Graphics::PixelFormat::createFormatCLUT8. This may only have any other result if <strong>the same game<\/strong> has already run an initSize with a different format, and is trying to switch formats during runtime.<\/li>\n<\/ul>\n<\/li>\n<li>If setup is successful, update the value that getScreenFormat returns to the value that was requested.\n<ul>\n<li>If format is supported by backend but not directly in hardware, ensure that graphics are converted in copyRectToScreen<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<li>If requested screen format is not supported, continue running in 256 color mode.<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n<h2><span class=\"mw-headline\"> Complete API reference <\/span><\/h2>\n<h3><span class=\"mw-headline\"> New functions <\/span><\/h3>\n<h4><span class=\"mw-headline\"> OSystem <\/span><\/h4>\n<ul>\n<li>Graphics::PixelFormat OSystem::getScreenFormat(void)\n<ul>\n<li>Returns the pixel format currently accepted for graphics from the engine.<\/li>\n<\/ul>\n<\/li>\n<li>Common::List&lt;Graphics::PixelFormat&gt; OSystem::getSupportedFormats(void)\n<ul>\n<li>Returns a list of all the pixel formats the backend can accept graphics in.<\/li>\n<li>The first item in this list <em><strong>must<\/strong><\/em> be the highest color graphics mode supported by the backend which is directly supported by the hardware.<\/li>\n<li>The remainder of the list <em><strong>must<\/strong><\/em> be in order of descending preference, such that the last item in the list is the one that the backend functions worst in.<\/li>\n<li>Backends which do not support fast conversion <em><strong>must<\/strong><\/em> put all modes directly supported in hardware, (and CLUT8), before modes that will require conversion during copyRectToScreen.<\/li>\n<li>Backends which support fast conversion <em>should<\/em> put larger colorspaces before smaller color spaces, but are not required to.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h4><span class=\"mw-headline\"> Graphics::PixelFormat <\/span><\/h4>\n<ul>\n<li>inline Graphics::PixelFormat Graphics::findCompatibleFormat(Common::List&lt;Graphics::PixelFormat&gt; backend, Common::List&lt;Graphics::PixelFormat&gt; frontend)\n<ul>\n<li>Returns the first entry on the backend list that also occurs in the frontend list, or CLUT8 if there is no matching format.<\/li>\n<\/ul>\n<\/li>\n<li>inline Graphics::PixelFormat (void)\n<ul>\n<li>creates an uninitialized PixelFormat.<\/li>\n<\/ul>\n<\/li>\n<li>inline Graphics::PixelFormat(byte BytesPerPixel, byte RBits, byte GBits, byte BBits, byte ABits, byte RShift, byte GShift, byte BShift, byte AShift)\n<ul>\n<li>creates an initialized PixelFormat.<\/li>\n<li>[_]Bits is the width in bits of the relevant channel\n<ul>\n<li>RBits = red bits, GBits = green bits, BBits = blue bits, ABits = alpha bits.<\/li>\n<\/ul>\n<\/li>\n<li>[_]Shift is the number (starting from 0) of the least significant bit in the relevant channel, which is equal to the bitshift required to make a channel.\n<ul>\n<li>In RGB565, RShift is 11, GShift is 5, and BShift is 0.<\/li>\n<li>In RGBA4444, RShift is 12, GShift is 8, BShift is 4, and AShift is 0.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<li>static inline Graphics::PixelFormat Graphics::PixelFormat::createFormatCLUT8(void)\n<ul>\n<li>creates a PixelFormat set to indicate 256 color paletted mode<\/li>\n<li>This method is provided for convenience, and is equivalent to initializing a Graphics::PixelFormat with the bytedepth of 1, component losses of 8, and component shifts of 0.\n<ul>\n<li>Which would be accomplished normally via Graphics::PixelFormat(1,8,8,8,8,0,0,0,0);<\/li>\n<\/ul>\n<\/li>\n<li>Because this methods are static, it can be called without creating a pixel format first\n<ul>\n<li>For instance, if (format == NULL) newFormat = Graphics::PixelFormat::createFormatCLUT8();<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h4><span class=\"mw-headline\"> Miscellaneous <\/span><\/h4>\n<ul>\n<li>bool Graphics::crossBlit(byte *dst, const byte *src, int dstpitch, int srcpitch, int w, int h, Graphics::PixelFormat dstFmt, Graphics::PixelFormat srcFmt)\n<ul>\n<li>blits a rectangle from a \u201csurface\u201d in srcFmt to a \u201csurface\u201d in dstFmt<\/li>\n<li>returns false if the blit fails (due to unsupported format conversion)<\/li>\n<li>returns true if the blit succeeds<\/li>\n<li>can convert the rectangle in place if src and dst are the same, and srcFmt and dstFmt have the same bytedepth<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h3><span class=\"mw-headline\"> Modified functions <\/span><\/h3>\n<h3><span class=\"mw-headline\">engine<\/span><\/h3>\n<ul>\n<li>void initGraphics(int width, int height, bool defaultTo1xScaler, const Graphics::PixelFormat *format)\n<ul>\n<li>Now takes a format parameter, which is a pointer to a requested pixelformat<\/li>\n<li>Uses top item in backend\u2019s getSupportedFormats list if format is NULL<\/li>\n<li>Now displays a warning if it recieves OSystem::kTransactionFormatNotSupported in return from endGFXTransaction<\/li>\n<li>Now overloaded to simplify initialization for the three engine types:<\/li>\n<\/ul>\n<\/li>\n<li>void initGraphics(int width, int height, bool defaultTo1xScaler)\n<ul>\n<li>A wrapper which sets format as a pointer to Graphics::PixelFormat::createFormatCLUT8();<\/li>\n<\/ul>\n<\/li>\n<li>void initGraphics(int width, int height, bool defaultTo1xScaler, const Commmon::List&lt;Graphics::PixelFormat&gt; &amp;formatList)\n<ul>\n<li>A wrapper which sets format as a pointer to the return value from Graphics::findCompatibleFormat(OSystem::getSupportedFormats(),formatList)<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h4><span class=\"mw-headline\">OSystem<\/span><\/h4>\n<ul>\n<li>virtual void OSystem::initSize(uint width, uint height, Graphics::PixelFormat *format = NULL)\n<ul>\n<li>Can now take a format parameter, which is a pointer to a requested pixelformat, and defaults to NULL<\/li>\n<li>Uses 256 color mode if format is NULL<\/li>\n<\/ul>\n<\/li>\n<li>OSystem::TransactionError OSystem::endGFXTransaction(void)\n<ul>\n<li><em><strong>Must<\/strong><\/em> now return kTransactionFormatNotSupported if the backend fails in an attempt to initialize to a new pixel format during a GFX transaction.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h4><span class=\"mw-headline\">CursorMan<\/span><\/h4>\n<ul>\n<li>void Graphics::CursorManager::pushCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, int targetScale, Graphics::PixelFormat *format)\n<ul>\n<li>Can now take a format parameter, which is a pointer to a Graphics::PixelFormat describing the pixel format of the cursor graphic, and defaults to NULL.<\/li>\n<li>Uses 256 color mode if format is NULL<\/li>\n<\/ul>\n<\/li>\n<li>void Graphics::CursorManager::replaceCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, int targetScale, Graphics::PixelFormat *format)\n<ul>\n<li>Can now take a format parameter, which is a pointer to a Graphics::PixelFormat describing the pixel format of the cursor graphic, and defaults to NULL.<\/li>\n<li>Uses 256 color mode if format is NULL<\/li>\n<\/ul>\n<\/li>\n<li>Graphics::CursorManager::Cursor(const byte *data, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor = 0xFFFFFFFF, int targetScale = 1, Graphics::PixelFormat format = Graphics::PixelFormat::createFormatCLUT8())\n<ul>\n<li>Can now take a format parameter, which is a Graphics::PixelFormat describing the pixel format of the cursor graphic, and defaults to 256 color mode.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h3><span class=\"mw-headline\"> Modified Types <\/span><\/h3>\n<ul>\n<li>enum Common::Error\n<ul>\n<li>Now includes a kUnsupportedColorMode value, for engines which get unsupported pixel formats after a format change request fails.<\/li>\n<\/ul>\n<\/li>\n<li>enum OSystem::TransactionError\n<ul>\n<li>Now includes a kTransactionFormatNotSupported value, for backends to announce failure to supply game screen with requested pixel format.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Note: the following is copied from the source of the wiki reference article I just wrote for it. Introduction This page is meant as a reference for developers implementing the Truecolor API in their engine and backend modules. This page will provide a complete spec of API requirements and suggestions, as well as a protocol [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-77","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/blogs.scummvm.org\/upthorn\/wp-json\/wp\/v2\/posts\/77","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blogs.scummvm.org\/upthorn\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.scummvm.org\/upthorn\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.scummvm.org\/upthorn\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.scummvm.org\/upthorn\/wp-json\/wp\/v2\/comments?post=77"}],"version-history":[{"count":2,"href":"https:\/\/blogs.scummvm.org\/upthorn\/wp-json\/wp\/v2\/posts\/77\/revisions"}],"predecessor-version":[{"id":80,"href":"https:\/\/blogs.scummvm.org\/upthorn\/wp-json\/wp\/v2\/posts\/77\/revisions\/80"}],"wp:attachment":[{"href":"https:\/\/blogs.scummvm.org\/upthorn\/wp-json\/wp\/v2\/media?parent=77"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.scummvm.org\/upthorn\/wp-json\/wp\/v2\/categories?post=77"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.scummvm.org\/upthorn\/wp-json\/wp\/v2\/tags?post=77"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}