aboutsummaryrefslogtreecommitdiff
path: root/src/dtx
diff options
context:
space:
mode:
Diffstat (limited to 'src/dtx')
-rw-r--r--src/dtx/CMakeLists.txt4
-rw-r--r--src/dtx/dtx.cpp207
-rw-r--r--src/dtx/dtx.h37
3 files changed, 248 insertions, 0 deletions
diff --git a/src/dtx/CMakeLists.txt b/src/dtx/CMakeLists.txt
new file mode 100644
index 0000000..40a8c1f
--- /dev/null
+++ b/src/dtx/CMakeLists.txt
@@ -0,0 +1,4 @@
+target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_LIST_DIR})
+target_sources(${PROJECT_NAME} PRIVATE
+ dtx.cpp
+)
diff --git a/src/dtx/dtx.cpp b/src/dtx/dtx.cpp
new file mode 100644
index 0000000..463bb1d
--- /dev/null
+++ b/src/dtx/dtx.cpp
@@ -0,0 +1,207 @@
+#include <dtx.h>
+#include <rez/file.h>
+#include <GL/gl.h>
+#include <GL/glext.h>
+#include <cstdint>
+#include <cstdio>
+#include <iostream>
+
+int dtx::draw() const
+{
+ int ret = 0;
+ GLenum error;
+
+ glEnable(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, texture);
+
+ if (bpp == bpp_s3tc_dxt5)
+ {
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA);
+ }
+
+ glBegin(GL_QUADS);
+ glTexCoord2f(0.0f, 0.0f); glVertex2f(-0.5f, 0.5f);
+ glTexCoord2f(0.0f, 1.0f); glVertex2f(0.5f, 0.5f);
+ glTexCoord2f(1.0f, 1.0f); glVertex2f(0.5f, -0.5f);
+ glTexCoord2f(1.0f, 0.0f); glVertex2f(-0.5f, -0.5f);
+ glEnd();
+
+ glFlush();
+ glDisable(GL_BLEND);
+ glDisable(GL_TEXTURE_2D);
+
+ while ((error = glGetError()) != GL_NO_ERROR)
+ {
+ ret = -1;
+ fprintf(stderr, "%s:%d: glGetError returned %x\n", __FILE__,
+ __LINE__, error);
+ }
+
+ return ret;
+}
+
+int dtx::parse(rez::file &f)
+{
+ uint32_t restype, uversion;
+
+ if (f.read_le(restype) || f.read_le(uversion))
+ return -1;
+
+ const uint32_t exp_type = 0;
+ const int32_t exp_version = -5;
+ int32_t version = uversion;
+
+ if (restype != exp_type)
+ {
+ std::cerr << f.path() << ": invalid resource type " << restype
+ << ", expected " << exp_type << '\n';
+ return -1;
+ }
+ else if (version != exp_version)
+ {
+ std::cerr << f.path() << ": invalid or unsupported version " << version
+ << ", expected " << exp_version << '\n';
+ return -1;
+ }
+
+ const uint16_t max_mipmaps = 8;
+ uint16_t w, h, n_mipmaps, n_sections, tex_angle;
+ uint32_t iflags, userflags, tex_scale;
+ unsigned char tex_group, n_rtmipmaps, bpp, mipmap_off_nc, mipmap_off,
+ tex_prio;
+ char cmd[128];
+
+ if (f.read_le(w)
+ || f.read_le(h)
+ || f.read_le(n_mipmaps)
+ || f.read_le(n_sections))
+ {
+ std::cerr << f.path() << ": failed to read header\n";
+ return -1;
+ }
+ else if (!n_mipmaps || n_mipmaps > max_mipmaps)
+ {
+ std::cerr << f.path() << ": invalid number of mipmaps: "
+ << n_mipmaps << '\n';
+ return -1;
+ }
+ else if (f.read_le(iflags) || f.read_le(userflags))
+ {
+ std::cerr << f.path() << ": failed to read flags\n";
+ return -1;
+ }
+ else if (f.read(tex_group)
+ || f.read(n_rtmipmaps)
+ || f.read(bpp)
+ || f.read(mipmap_off_nc)
+ || f.read(mipmap_off)
+ || f.read(tex_prio)
+ || f.read_le(tex_scale)
+ || f.read_le(tex_angle))
+ {
+ std::cerr << f.path() << ": failed to read extra data\n";
+ return -1;
+ }
+ else if (bpp != bpp::bpp_32
+ && bpp != bpp::bpp_s3tc_dxt1
+ && bpp != bpp::bpp_s3tc_dxt5)
+ {
+ std::cerr << f.path() << ": invalid or unsupported bpp "
+ << std::to_string(bpp) << ", expected " << bpp::bpp_s3tc_dxt1
+ << ", " << bpp::bpp_32 << " or " << bpp::bpp_s3tc_dxt5 << '\n';
+ return -1;
+ }
+ else if (f.read(cmd, sizeof cmd))
+ {
+ std::cerr << f.path() << ": failed to read cmd\n";
+ return -1;
+ }
+
+ this->bpp = static_cast<enum bpp>(bpp);
+ this->w = w;
+ this->h = h;
+
+ glGenTextures(1, &texture);
+ glBindTexture(GL_TEXTURE_2D, texture);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+
+ GLint level = 0;
+
+ for (uint32_t i = 0; i < n_mipmaps; i++)
+ {
+ GLenum format;
+ unsigned long n_pix = (w >> i) * (h >> i);
+ pixdata mipmap;
+
+ switch (this->bpp)
+ {
+ case bpp_32:
+ format = GL_BGRA;
+ n_pix *= sizeof (uint32_t);
+ break;
+
+ case bpp_s3tc_dxt1:
+ format = GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
+ n_pix >>= 1;
+ break;
+
+ case bpp_s3tc_dxt5:
+ format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
+ break;
+
+ default:
+ return -1;
+ }
+
+ mipmap.reserve(n_pix);
+
+ while (n_pix)
+ {
+ unsigned char buf[BUFSIZ];
+ size_t n = n_pix > sizeof buf ? sizeof buf : n_pix;
+
+ if (f.read(buf, n))
+ return -1;
+
+ mipmap.insert(mipmap.end(), buf, &buf[n]);
+ n_pix -= n;
+ }
+
+ GLenum error;
+ int ret = 0;
+
+ if (bpp == bpp::bpp_s3tc_dxt1 || bpp == bpp::bpp_s3tc_dxt5)
+ glCompressedTexImage2D(GL_TEXTURE_2D, level++, format,
+ w >> i, h >> i, 0, mipmap.size(), mipmap.data());
+ else if (bpp == bpp::bpp_32)
+ glTexImage2D(GL_TEXTURE_2D, level++, format, w >> i, h >> i, 0,
+ format, GL_UNSIGNED_BYTE, mipmap.data());
+
+ while ((error = glGetError()) != GL_NO_ERROR)
+ {
+ fprintf(stderr, "%s:%d: glGetError returned %x\n", __FILE__,
+ __LINE__, error);
+ ret = -1;
+ }
+
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+dtx::~dtx()
+{
+ if (texture)
+ glDeleteTextures(1, &texture);
+}
+
+dtx::dtx() :
+ texture(0)
+{
+}
diff --git a/src/dtx/dtx.h b/src/dtx/dtx.h
new file mode 100644
index 0000000..24a7a7b
--- /dev/null
+++ b/src/dtx/dtx.h
@@ -0,0 +1,37 @@
+#ifndef DTX_H
+#define DTX_H
+
+#include <rez/file.h>
+#include <GL/gl.h>
+
+class dtx
+{
+public:
+ typedef std::vector<unsigned char> pixdata;
+
+ dtx();
+ ~dtx();
+ int parse(rez::file &f);
+ int draw() const;
+
+private:
+
+ enum bpp
+ {
+ bpp_8_pal,
+ bpp_8,
+ bpp_16,
+ bpp_32,
+ bpp_s3tc_dxt1,
+ bpp_s3tc_dxt3,
+ bpp_s3tc_dxt5,
+ bpp_32_pal,
+
+ n_bpp
+ } bpp;
+
+ GLuint texture;
+ unsigned w, h;
+};
+
+#endif