diff --git a/Makefile b/Makefile index e055d4d5b6..485f8522f0 100644 --- a/Makefile +++ b/Makefile @@ -507,8 +507,6 @@ RNCPACK := $(TOOLS_DIR)/rncpack FILESIZER := $(TOOLS_DIR)/filesizer N64CKSUM := $(TOOLS_DIR)/n64cksum N64GRAPHICS := $(TOOLS_DIR)/n64graphics -N64GRAPHICS_CI := $(TOOLS_DIR)/n64graphics_ci -BINPNG := $(TOOLS_DIR)/BinPNG.py TEXTCONV := $(TOOLS_DIR)/textconv AIFF_EXTRACT_CODEBOOK := $(TOOLS_DIR)/aiff_extract_codebook VADPCM_ENC := $(TOOLS_DIR)/vadpcm_enc @@ -695,28 +693,27 @@ $(BUILD_DIR)/src/game/puppycam2.o: $(BUILD_DIR)/include/text_strings.h #==============================================================================# TEXTURE_ENCODING := u8 -# Convert PNGs to RGBA32, RGBA16, IA16, IA8, IA4, IA1, I8, I4 binary files -$(BUILD_DIR)/%: %.png - $(call print,Converting:,$<,$@) - $(V)$(N64GRAPHICS) -s raw -i $@ -g $< -f $(lastword $(subst ., ,$@)) +define convert_texture +$$(BUILD_DIR)/%$(1).inc.c: %$(1).png + $$(call print,Converting:,$$<,$$@) + $$(V)$$(N64GRAPHICS) -s $$(TEXTURE_ENCODING) -i $$@ -g $$< $(2) +$$(BUILD_DIR)/%$(1).preswap.inc.c: %$(1).preswap.png + $$(call print,Converting:,$$<,$$@) + $$(V)$$(N64GRAPHICS) -s $$(TEXTURE_ENCODING) -i $$@ -g $$< $(2) -S +endef -$(BUILD_DIR)/%.preswap.inc.c: %.preswap.png - $(call print,Converting:,$<,$@) - $(V)$(N64GRAPHICS) -s $(TEXTURE_ENCODING) -i $@ -g $< -f $(lastword ,$(subst ., ,$*)) -S +$(eval $(call convert_texture,,-f $$(lastword ,$$(subst ., ,$$*)))) +$(eval $(call convert_texture,.ci4_rgba16,-p $$(patsubst %.inc.c,%.pal,$$@) -f ci4 -c rgba16)) +$(eval $(call convert_texture,.ci8_rgba16,-p $$(patsubst %.inc.c,%.pal,$$@) -f ci8 -c rgba16)) +$(eval $(call convert_texture,.ci4_ia16,-p $$(patsubst %.inc.c,%.pal,$$@) -f ci4 -c ia16)) +$(eval $(call convert_texture,.ci8_ia16,-p $$(patsubst %.inc.c,%.pal,$$@) -f ci8 -c ia16)) +$(eval $(call convert_texture,.ci4,-p $$(patsubst %.ci4.inc.c,%.rgba16.pal,$$@) -f ci4 -c rgba16)) +$(eval $(call convert_texture,.ci8,-p $$(patsubst %.ci8.inc.c,%.rgba16.pal,$$@) -f ci8 -c rgba16)) -$(BUILD_DIR)/%.inc.c: %.png +# Convert image files to binary - used primarily for IPL3 "IA1" textures +$(BUILD_DIR)/%: %.png $(call print,Converting:,$<,$@) - $(V)$(N64GRAPHICS) -s $(TEXTURE_ENCODING) -i $@ -g $< -f $(lastword ,$(subst ., ,$*)) - -# Color Index CI8 -$(BUILD_DIR)/%.ci8.inc.c: %.ci8.png - $(call print,Converting CI:,$<,$@) - $(V)$(BINPNG) $< $@ 8 - -# Color Index CI4 -$(BUILD_DIR)/%.ci4.inc.c: %.ci4.png - $(call print,Converting CI:,$<,$@) - $(V)$(BINPNG) $< $@ 4 + $(V)$(N64GRAPHICS) -s raw -i $@ -g $< -f $(lastword $(subst ., ,$@)) #==============================================================================# diff --git a/tools/.gitignore b/tools/.gitignore index 72215f5e27..613a6a6c29 100644 --- a/tools/.gitignore +++ b/tools/.gitignore @@ -8,7 +8,6 @@ /mio0 /n64cksum /n64graphics -/n64graphics_ci /patch_elf_32bit /rncpack /slienc diff --git a/tools/BinPNG.py b/tools/BinPNG.py deleted file mode 100755 index 6f9c95c598..0000000000 --- a/tools/BinPNG.py +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env python3 -import struct -import png -import math -from bitstring import * -import sys -#convert png to bin - -def MakeCI(file,Bpp): - r = png.Reader(file) - re = r.read() - Pal = re[3]['palette'] - Pbin = [] - shifts = [3,3,3,7] - for p in Pal: - b = [a>>s for a,s in zip(p,shifts)] - if len(p)==4: - b = pack('3*uint:5,uint:1',*b) - else: - b = pack('3*uint:5,uint:1',*b,1) - Pbin.append(b.bytes) - bin = [] - for p in re[2]: - for w in range(0,re[0],(8//Bpp)): - b = p[w:w+(8//Bpp)] - b = pack('%d*uint:%d'%((8//Bpp),Bpp),*b) - bin.append(b.bytes) - return [bin,Pbin] - -if __name__=='__main__': - [texture,palette] = MakeCI(sys.argv[1],int(sys.argv[3])) - pname = sys.argv[2].split('.') - pname.insert(2,'pal') - pname = pname[:3] - pname = '.'.join(pname) - p = open(pname,'w') - t = open(sys.argv[2],'w') - [t.write('0x{:02X},'.format(tex[0])) for tex in texture] - [p.write('0x{:02X},0x{:02X},'.format(pal[0],pal[1])) for pal in palette] - t.close() - p.close() \ No newline at end of file diff --git a/tools/Makefile b/tools/Makefile index ce2220f313..c51b6c0a3b 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -7,7 +7,7 @@ CC := gcc CXX := g++ CFLAGS := -I. -O2 -s LDFLAGS := -lm -ALL_PROGRAMS := armips filesizer lz4tpack deflatepack rncpack n64graphics n64graphics_ci mio0 slienc n64cksum textconv aifc_decode aiff_extract_codebook vadpcm_enc tabledesign extract_data_for_mio skyconv +ALL_PROGRAMS := armips filesizer lz4tpack deflatepack rncpack n64graphics mio0 slienc n64cksum textconv aifc_decode aiff_extract_codebook vadpcm_enc tabledesign extract_data_for_mio skyconv LIBAUDIOFILE := audiofile/libaudiofile.a ifneq ($(shell uname), Darwin) @@ -44,8 +44,6 @@ rncpack_SOURCES := rncpack.c n64graphics_SOURCES := n64graphics.c utils.c n64graphics_CFLAGS := -DN64GRAPHICS_STANDALONE -n64graphics_ci_SOURCES := n64graphics_ci_dir/n64graphics_ci.c n64graphics_ci_dir/exoquant/exoquant.c n64graphics_ci_dir/utils.c - mio0_SOURCES := libmio0.c mio0_CFLAGS := -DMIO0_STANDALONE diff --git a/tools/n64graphics.c b/tools/n64graphics.c index c0014d0ccc..82568fb016 100644 --- a/tools/n64graphics.c +++ b/tools/n64graphics.c @@ -760,7 +760,7 @@ static void print_usage(void) " -w WIDTH export texture width (default: %d)\n" " -h HEIGHT export texture height (default: %d)\n" " -r ROTATE rotate envmap texture for rgba extraction only (default: false)\n" - " -S swap words on odd lines (importing only, does not support ci4, ci8, or ia1 formats)\n" + " -S swap words on odd lines (importing only, does not support ia1 format)\n" "CI arguments:\n" " -c CI_FORMAT CI palette format: rgba16, ia16 (default: %s)\n" " -p PAL_FILE palette binary file to import/export from/to\n" @@ -1020,9 +1020,6 @@ int main(int argc, char *argv[]) if (config.format.depth == 1) { ERROR("Word swapping unavailable for IA1 texture format\n"); return EXIT_FAILURE; - } else if (config.format.format == IMG_FORMAT_CI) { - ERROR("Word swapping unavailable for CI texture formats\n"); - return EXIT_FAILURE; } int swap_bytes = (config.format.depth == 32) ? 8 : 4; diff --git a/tools/n64graphics_ci_dir/LICENSE b/tools/n64graphics_ci_dir/LICENSE deleted file mode 100644 index 3c22ec7995..0000000000 --- a/tools/n64graphics_ci_dir/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2019 David Benepe - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/tools/n64graphics_ci_dir/README.md b/tools/n64graphics_ci_dir/README.md deleted file mode 100644 index 14bda5da64..0000000000 --- a/tools/n64graphics_ci_dir/README.md +++ /dev/null @@ -1,29 +0,0 @@ -# n64graphics_ci - -Allows you to convert PNG image files to/from N64 CI format. This is temporary until queueRAM adds CI support into his official n64graphics tool. This tool does not process RGBA, IA, or I textures, use n64graphics for those. - -CI4 textures will always assume a 16 color palette is used, and CI8 textures a 256 palette is used. The palette will be generated as a seperate file. The palette file will be named after the CI filename, but postpended with `.pal`. - -## Libraries Used (All MIT licensed) - -* **Exoquant** by Dennis Ranke, for color reduction. https://github.com/exoticorn/exoquant -* **stb** by Sean Barrett, for loading PNG images. https://github.com/nothings/stb - -## PNG -> CI4 + Palette - -`./n64graphics_ci -i image.ci4 -g image.png -f ci4` - -## CI4 + Palette -> 32x32 PNG - -`./n64graphics_ci -e image.ci4 -g image.ci4.png -f ci4 -w 32 -h 32` - -## PNG -> CI8 + Palette - -`./n64graphics_ci -i image.ci8 -g image.png -f ci8` - -## CI8 + Palette -> 32x32 PNG - -`./n64graphics_ci -e image.ci8 -g image.ci8.png -f ci8 -w 32 -h 32` - -## Comparision -![alt text](https://i.imgur.com/r3PhZp0.png) diff --git a/tools/n64graphics_ci_dir/exoquant/exoquant.c b/tools/n64graphics_ci_dir/exoquant/exoquant.c deleted file mode 100644 index 95d83191f6..0000000000 --- a/tools/n64graphics_ci_dir/exoquant/exoquant.c +++ /dev/null @@ -1,707 +0,0 @@ -/* -ExoQuant v0.7 - -Copyright (c) 2004 Dennis Ranke - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -#include "exoquant.h" -#include -#include -#include - -#ifndef NULL -#define NULL (0) -#endif - -#define SCALE_R 1.0f -#define SCALE_G 1.2f -#define SCALE_B 0.8f -#define SCALE_A 1.0f - -exq_data *exq_init() -{ - int i; - exq_data *pExq; - - pExq = (exq_data*)malloc(sizeof(exq_data)); - - for(i = 0; i < EXQ_HASH_SIZE; i++) - pExq->pHash[i] = NULL; - - pExq->numColors = 0; - pExq->optimized = 0; - pExq->transparency = 1; - pExq->numBitsPerChannel = 8; - - return pExq; -} - -void exq_no_transparency(exq_data *pExq) -{ - pExq->transparency = 0; -} - -void exq_free(exq_data *pExq) -{ - int i; - exq_histogram *pCur, *pNext; - - for(i = 0; i < EXQ_HASH_SIZE; i++) - for(pCur = pExq->pHash[i]; pCur != NULL; pCur = pNext) - { - pNext = pCur->pNextInHash; - free(pCur); - } - - free(pExq); -} - -static unsigned int exq_make_hash(unsigned int rgba) -{ - rgba -= (rgba >> 13) | (rgba << 19); - rgba -= (rgba >> 13) | (rgba << 19); - rgba -= (rgba >> 13) | (rgba << 19); - rgba -= (rgba >> 13) | (rgba << 19); - rgba -= (rgba >> 13) | (rgba << 19); - rgba &= EXQ_HASH_SIZE - 1; - return rgba; -} - -void exq_feed(exq_data *pExq, unsigned char *pData, int nPixels) -{ - int i; - unsigned int hash; - unsigned char r, g, b, a; - exq_histogram *pCur; - unsigned char channelMask = 0xff00 >> pExq->numBitsPerChannel; - - for(i = 0; i < nPixels; i++) - { - r = *pData++; g = *pData++; b = *pData++; a = *pData++; - hash = exq_make_hash(((unsigned int)r) | (((unsigned int)g) << 8) | (((unsigned int)b) << 16) | (((unsigned int)a) << 24)); - - pCur = pExq->pHash[hash]; - while(pCur != NULL && (pCur->ored != r || pCur->ogreen != g || - pCur->oblue != b || pCur->oalpha != a)) - pCur = pCur->pNextInHash; - - if(pCur != NULL) - pCur->num++; - else - { - pCur = (exq_histogram*)malloc(sizeof(exq_histogram)); - pCur->pNextInHash = pExq->pHash[hash]; - pExq->pHash[hash] = pCur; - pCur->ored = r; pCur->ogreen = g; pCur->oblue = b; pCur->oalpha = a; - r &= channelMask; g &= channelMask; b &= channelMask; - pCur->color.r = r / 255.0f * SCALE_R; - pCur->color.g = g / 255.0f * SCALE_G; - pCur->color.b = b / 255.0f * SCALE_B; - pCur->color.a = a / 255.0f * SCALE_A; - - if(pExq->transparency) - { - pCur->color.r *= pCur->color.a; - pCur->color.g *= pCur->color.a; - pCur->color.b *= pCur->color.a; - } - - pCur->num = 1; - pCur->palIndex = -1; - pCur->ditherScale.r = pCur->ditherScale.g = pCur->ditherScale.b = - pCur->ditherScale.a = -1; - pCur->ditherIndex[0] = pCur->ditherIndex[1] = pCur->ditherIndex[2] = - pCur->ditherIndex[3] = -1; - } - } -} - -void exq_quantize(exq_data *pExq, int nColors) -{ - exq_quantize_ex(pExq, nColors, 0); -} - -void exq_quantize_hq(exq_data *pExq, int nColors) -{ - exq_quantize_ex(pExq, nColors, 1); -} - -void exq_quantize_ex(exq_data *pExq, int nColors, int hq) -{ - int besti; - exq_float beste; - exq_histogram *pCur, *pNext; - int i, j; - - if(nColors > 256) - nColors = 256; - - if(pExq->numColors == 0) - { - pExq->node[0].pHistogram = NULL; - for(i = 0; i < EXQ_HASH_SIZE; i++) - for(pCur = pExq->pHash[i]; pCur != NULL; pCur = pCur->pNextInHash) - { - pCur->pNext = pExq->node[0].pHistogram; - pExq->node[0].pHistogram = pCur; - } - - exq_sum_node(&pExq->node[0]); - - pExq->numColors = 1; - } - - for(i = pExq->numColors; i < nColors; i++) - { - beste = 0; - besti = 0; - for(j = 0; j < i; j++) - if(pExq->node[j].vdif >= beste) - { - beste = pExq->node[j].vdif; - besti = j; - } - -// printf("node %d: %d, %f\n", besti, pExq->node[besti].num, beste); - - pCur = pExq->node[besti].pHistogram; - pExq->node[besti].pHistogram = NULL; - pExq->node[i].pHistogram = NULL; - while(pCur != NULL && pCur != pExq->node[besti].pSplit) - { - pNext = pCur->pNext; - pCur->pNext = pExq->node[i].pHistogram; - pExq->node[i].pHistogram = pCur; - pCur = pNext; - } - - while(pCur != NULL) - { - pNext = pCur->pNext; - pCur->pNext = pExq->node[besti].pHistogram; - pExq->node[besti].pHistogram = pCur; - pCur = pNext; - } - - exq_sum_node(&pExq->node[besti]); - exq_sum_node(&pExq->node[i]); - - pExq->numColors = i + 1; - if(hq) - exq_optimize_palette(pExq, 1); - } - - pExq->optimized = 0; -} - -exq_float exq_get_mean_error(exq_data *pExq) -{ - int i, n; - exq_float err; - - n = 0; - err = 0; - for(i = 0; i < pExq->numColors; i++) - { - n += pExq->node[i].num; - err += pExq->node[i].err; - } - - return sqrt(err / n) * 256; -} - -void exq_get_palette(exq_data *pExq, unsigned char *pPal, int nColors) -{ - int i, j; - exq_float r, g, b, a; - unsigned char channelMask = 0xff00 >> pExq->numBitsPerChannel; - - if(nColors > pExq->numColors) - nColors = pExq->numColors; - - if(!pExq->optimized) - exq_optimize_palette(pExq, 4); - - for(i = 0; i < nColors; i++) - { - r = pExq->node[i].avg.r; - g = pExq->node[i].avg.g; - b = pExq->node[i].avg.b; - a = pExq->node[i].avg.a; - - if(pExq->transparency == 1 && a != 0) - { - r /= a; g/= a; b/= a; - } - - pPal[0] = (unsigned char)(r / SCALE_R * 255.9f); - pPal[1] = (unsigned char)(g / SCALE_G * 255.9f); - pPal[2] = (unsigned char)(b / SCALE_B * 255.9f); - pPal[3] = (unsigned char)(a / SCALE_A * 255.9f); - - for(j = 0; j < 3; j++) - pPal[j] = (pPal[j] + (1 << (8 - pExq->numBitsPerChannel)) / 2) & channelMask; - pPal += 4; - } -} - -void exq_set_palette(exq_data *pExq, unsigned char *pPal, int nColors) -{ - int i; - - pExq->numColors = nColors; - - for(i = 0; i < nColors; i++) - { - pExq->node[i].avg.r = *pPal++ * SCALE_R / 255.9f; - pExq->node[i].avg.g = *pPal++ * SCALE_G / 255.9f; - pExq->node[i].avg.b = *pPal++ * SCALE_B / 255.9f; - pExq->node[i].avg.a = *pPal++ * SCALE_A / 255.9f; - } - - pExq->optimized = 1; -} - -void exq_sum_node(exq_node *pNode) -{ - int n, n2; - exq_color fsum, fsum2, vc, tmp, tmp2, sum, sum2; - exq_histogram *pCur; - exq_float isqrt, nv, v; - - n = 0; - fsum.r = fsum.g = fsum.b = fsum.a = 0; - fsum2.r = fsum2.g = fsum2.b = fsum2.a = 0; - - for(pCur = pNode->pHistogram; pCur != NULL; pCur = pCur->pNext) - { - n += pCur->num; - fsum.r += pCur->color.r * pCur->num; - fsum.g += pCur->color.g * pCur->num; - fsum.b += pCur->color.b * pCur->num; - fsum.a += pCur->color.a * pCur->num; - fsum2.r += pCur->color.r * pCur->color.r * pCur->num; - fsum2.g += pCur->color.g * pCur->color.g * pCur->num; - fsum2.b += pCur->color.b * pCur->color.b * pCur->num; - fsum2.a += pCur->color.a * pCur->color.a * pCur->num; - } - pNode->num = n; - if(n == 0) - { - pNode->vdif = 0; - pNode->err = 0; - return; - } - - pNode->avg.r = fsum.r / n; - pNode->avg.g = fsum.g / n; - pNode->avg.b = fsum.b / n; - pNode->avg.a = fsum.a / n; - - vc.r = fsum2.r - fsum.r * pNode->avg.r; - vc.g = fsum2.g - fsum.g * pNode->avg.g; - vc.b = fsum2.b - fsum.b * pNode->avg.b; - vc.a = fsum2.a - fsum.a * pNode->avg.a; - - v = vc.r + vc.g + vc.b + vc.a; - pNode->err = v; - pNode->vdif = -v; - - if(vc.r > vc.g && vc.r > vc.b && vc.r > vc.a) - exq_sort(&pNode->pHistogram, exq_sort_by_r); - else if(vc.g > vc.b && vc.g > vc.a) - exq_sort(&pNode->pHistogram, exq_sort_by_g); - else if(vc.b > vc.a) - exq_sort(&pNode->pHistogram, exq_sort_by_b); - else - exq_sort(&pNode->pHistogram, exq_sort_by_a); - - pNode->dir.r = pNode->dir.g = pNode->dir.b = pNode->dir.a = 0; - for(pCur = pNode->pHistogram; pCur != NULL; pCur = pCur->pNext) - { - tmp.r = (pCur->color.r - pNode->avg.r) * pCur->num; - tmp.g = (pCur->color.g - pNode->avg.g) * pCur->num; - tmp.b = (pCur->color.b - pNode->avg.b) * pCur->num; - tmp.a = (pCur->color.a - pNode->avg.a) * pCur->num; - if(tmp.r * pNode->dir.r + tmp.g * pNode->dir.g + - tmp.b * pNode->dir.b + tmp.a * pNode->dir.a < 0) - { - tmp.r = -tmp.r; - tmp.g = -tmp.g; - tmp.b = -tmp.b; - tmp.a = -tmp.a; - } - pNode->dir.r += tmp.r; - pNode->dir.g += tmp.g; - pNode->dir.b += tmp.b; - pNode->dir.a += tmp.a; - } - isqrt = 1 / sqrt(pNode->dir.r * pNode->dir.r + - pNode->dir.g * pNode->dir.g + pNode->dir.b * pNode->dir.b + - pNode->dir.a * pNode->dir.a); - pNode->dir.r *= isqrt; - pNode->dir.g *= isqrt; - pNode->dir.b *= isqrt; - pNode->dir.a *= isqrt; - - exq_sort_dir = pNode->dir; - exq_sort(&pNode->pHistogram, exq_sort_by_dir); - - sum.r = sum.g = sum.b = sum.a = 0; - sum2.r = sum2.g = sum2.b = sum2.a = 0; - n2 = 0; - pNode->pSplit = pNode->pHistogram; - for(pCur = pNode->pHistogram; pCur != NULL; pCur = pCur->pNext) - { - if(pNode->pSplit == NULL) - pNode->pSplit = pCur; - - n2 += pCur->num; - sum.r += pCur->color.r * pCur->num; - sum.g += pCur->color.g * pCur->num; - sum.b += pCur->color.b * pCur->num; - sum.a += pCur->color.a * pCur->num; - sum2.r += pCur->color.r * pCur->color.r * pCur->num; - sum2.g += pCur->color.g * pCur->color.g * pCur->num; - sum2.b += pCur->color.b * pCur->color.b * pCur->num; - sum2.a += pCur->color.a * pCur->color.a * pCur->num; - - if(n == n2) - break; - - tmp.r = sum2.r - sum.r*sum.r / n2; - tmp.g = sum2.g - sum.g*sum.g / n2; - tmp.b = sum2.b - sum.b*sum.b / n2; - tmp.a = sum2.a - sum.a*sum.a / n2; - tmp2.r = (fsum2.r - sum2.r) - (fsum.r-sum.r)*(fsum.r-sum.r) / (n - n2); - tmp2.g = (fsum2.g - sum2.g) - (fsum.g-sum.g)*(fsum.g-sum.g) / (n - n2); - tmp2.b = (fsum2.b - sum2.b) - (fsum.b-sum.b)*(fsum.b-sum.b) / (n - n2); - tmp2.a = (fsum2.a - sum2.a) - (fsum.a-sum.a)*(fsum.a-sum.a) / (n - n2); - - nv = tmp.r + tmp.g + tmp.b + tmp.a + tmp2.r + tmp2.g + tmp2.b + tmp2.a; - if(-nv > pNode->vdif) - { - pNode->vdif = -nv; - pNode->pSplit = NULL; - } - } - - if(pNode->pSplit == pNode->pHistogram) - pNode->pSplit = pNode->pSplit->pNext; - - pNode->vdif += v; -// printf("error sum: %f, vdif: %f\n", pNode->err, pNode->vdif); -} - -void exq_optimize_palette(exq_data *pExq, int iter) -{ - int n, i, j; - exq_histogram *pCur; - - pExq->optimized = 1; - - for(n = 0; n < iter; n++) - { - for(i = 0; i < pExq->numColors; i++) - pExq->node[i].pHistogram = NULL; - - for(i = 0; i < EXQ_HASH_SIZE; i++) - for(pCur = pExq->pHash[i]; pCur != NULL; pCur = pCur->pNextInHash) - { - j = exq_find_nearest_color(pExq, &pCur->color); - pCur->pNext = pExq->node[j].pHistogram; - pExq->node[j].pHistogram = pCur; - } - - for(i = 0; i < pExq->numColors; i++) - exq_sum_node(&pExq->node[i]); - } -} - -void exq_map_image(exq_data *pExq, int nPixels, unsigned char *pIn, - unsigned char *pOut) -{ - int i; - exq_color c; - exq_histogram *pHist; - - if(!pExq->optimized) - exq_optimize_palette(pExq, 4); - - for(i = 0; i < nPixels; i++) - { - pHist = exq_find_histogram(pExq, pIn); - if(pHist != NULL && pHist->palIndex != -1) - { - *pOut++ = (unsigned char)pHist->palIndex; - pIn += 4; - } - else - { - c.r = *pIn++ / 255.0f * SCALE_R; - c.g = *pIn++ / 255.0f * SCALE_G; - c.b = *pIn++ / 255.0f * SCALE_B; - c.a = *pIn++ / 255.0f * SCALE_A; - - if(pExq->transparency) - { - c.r *= c.a; c.g *= c.a; c.b *= c.a; - } - - *pOut = exq_find_nearest_color(pExq, &c); - if(pHist != NULL) - pHist->palIndex = *pOut; - pOut++; - } - } -} - -void exq_map_image_ordered(exq_data *pExq, int width, int height, - unsigned char *pIn, unsigned char *pOut) -{ - exq_map_image_dither(pExq, width, height, pIn, pOut, 1); -} - -void exq_map_image_random(exq_data *pExq, int nPixels, - unsigned char *pIn, unsigned char *pOut) -{ - exq_map_image_dither(pExq, nPixels, 1, pIn, pOut, 0); -} - -void exq_map_image_dither(exq_data *pExq, int width, int height, - unsigned char *pIn, unsigned char *pOut, int ordered) -{ - int x, y, i, j, d; - exq_color p, scale, tmp; - exq_histogram *pHist; - const exq_float dither_matrix[4] = { -0.375, 0.125, 0.375, -0.125 }; - - if(!pExq->optimized) - exq_optimize_palette(pExq, 4); - - for(y = 0; y < height; y++) - for(x = 0; x < width; x++) - { - if(ordered) - d = (x & 1) + (y & 1) * 2; - else - d = rand() & 3; - pHist = exq_find_histogram(pExq, pIn); - p.r = *pIn++ / 255.0f * SCALE_R; - p.g = *pIn++ / 255.0f * SCALE_G; - p.b = *pIn++ / 255.0f * SCALE_B; - p.a = *pIn++ / 255.0f * SCALE_A; - - if(pExq->transparency) - { - p.r *= p.a; p.g *= p.a; p.b *= p.a; - } - - if(pHist == NULL || pHist->ditherScale.r < 0) - { - i = exq_find_nearest_color(pExq, &p); - scale.r = pExq->node[i].avg.r - p.r; - scale.g = pExq->node[i].avg.g - p.g; - scale.b = pExq->node[i].avg.b - p.b; - scale.a = pExq->node[i].avg.a - p.a; - tmp.r = p.r - scale.r / 3; - tmp.g = p.g - scale.g / 3; - tmp.b = p.b - scale.b / 3; - tmp.a = p.a - scale.a / 3; - j = exq_find_nearest_color(pExq, &tmp); - if(i == j) - { - tmp.r = p.r - scale.r * 3; - tmp.g = p.g - scale.g * 3; - tmp.b = p.b - scale.b * 3; - tmp.a = p.a - scale.a * 3; - j = exq_find_nearest_color(pExq, &tmp); - } - if(i != j) - { - scale.r = (pExq->node[j].avg.r - pExq->node[i].avg.r) * 0.8f; - scale.g = (pExq->node[j].avg.g - pExq->node[i].avg.g) * 0.8f; - scale.b = (pExq->node[j].avg.b - pExq->node[i].avg.b) * 0.8f; - scale.a = (pExq->node[j].avg.a - pExq->node[i].avg.a) * 0.8f; - if(scale.r < 0) scale.r = -scale.r; - if(scale.g < 0) scale.g = -scale.g; - if(scale.b < 0) scale.b = -scale.b; - if(scale.a < 0) scale.a = -scale.a; - } - else - scale.r = scale.g = scale.b = scale.a = 0; - - if(pHist != NULL) - { - pHist->ditherScale.r = scale.r; - pHist->ditherScale.g = scale.g; - pHist->ditherScale.b = scale.b; - pHist->ditherScale.a = scale.a; - } - } - else - { - scale.r = pHist->ditherScale.r; - scale.g = pHist->ditherScale.g; - scale.b = pHist->ditherScale.b; - scale.a = pHist->ditherScale.a; - } - - if(pHist != NULL && pHist->ditherIndex[d] >= 0) - *pOut++ = (unsigned char)pHist->ditherIndex[d]; - else - { - tmp.r = p.r + scale.r * dither_matrix[d]; - tmp.g = p.g + scale.g * dither_matrix[d]; - tmp.b = p.b + scale.b * dither_matrix[d]; - tmp.a = p.a + scale.a * dither_matrix[d]; - *pOut = exq_find_nearest_color(pExq, &tmp); - if(pHist != NULL) - pHist->ditherIndex[d] = *pOut; - pOut++; - } - } -} - -exq_histogram *exq_find_histogram(exq_data *pExq, unsigned char *pCol) -{ - unsigned int hash; - int r, g, b, a; - exq_histogram *pCur; - - r = *pCol++; g = *pCol++; b = *pCol++; a = *pCol++; - hash = exq_make_hash(((unsigned int)r) | (((unsigned int)g) << 8) | (((unsigned int)b) << 16) | (((unsigned int)a) << 24)); - - pCur = pExq->pHash[hash]; - while(pCur != NULL && (pCur->ored != r || pCur->ogreen != g || - pCur->oblue != b || pCur->oalpha != a)) - pCur = pCur->pNextInHash; - - return pCur; -} - -unsigned char exq_find_nearest_color(exq_data *pExq, exq_color *pColor) -{ - exq_float bestv; - int besti, i; - exq_color dif; - - bestv = 16; - besti = 0; - for(i = 0; i < pExq->numColors; i++) - { - dif.r = pColor->r - pExq->node[i].avg.r; - dif.g = pColor->g - pExq->node[i].avg.g; - dif.b = pColor->b - pExq->node[i].avg.b; - dif.a = pColor->a - pExq->node[i].avg.a; - if(dif.r*dif.r + dif.g*dif.g + dif.b*dif.b + dif.a*dif.a < bestv) - { - bestv = dif.r*dif.r + dif.g*dif.g + dif.b*dif.b + dif.a*dif.a; - besti = i; - } - } - - return (unsigned char)besti; -} - -void exq_sort(exq_histogram **ppHist, exq_float (*sortfunc)(const exq_histogram *pHist)) -{ - exq_histogram *pLow, *pHigh, *pCur, *pNext; - int n = 0; - exq_float sum = 0; - - for(pCur = *ppHist; pCur != NULL; pCur = pCur->pNext) - { - n++; - sum += sortfunc(pCur); - } - - if(n < 2) - return; - - sum /= n; - - pLow = pHigh = NULL; - for(pCur = *ppHist; pCur != NULL; pCur = pNext) - { - pNext = pCur->pNext; - if(sortfunc(pCur) < sum) - { - pCur->pNext = pLow; - pLow = pCur; - } - else - { - pCur->pNext = pHigh; - pHigh = pCur; - } - } - - if(pLow == NULL) - { - *ppHist = pHigh; - return; - } - if(pHigh == NULL) - { - *ppHist = pLow; - return; - } - - exq_sort(&pLow, sortfunc); - exq_sort(&pHigh, sortfunc); - - *ppHist = pLow; - while(pLow->pNext != NULL) - pLow = pLow->pNext; - - pLow->pNext = pHigh; -} - -exq_float exq_sort_by_r(const exq_histogram *pHist) -{ - return pHist->color.r; -} - -exq_float exq_sort_by_g(const exq_histogram *pHist) -{ - return pHist->color.g; -} - -exq_float exq_sort_by_b(const exq_histogram *pHist) -{ - return pHist->color.b; -} - -exq_float exq_sort_by_a(const exq_histogram *pHist) -{ - return pHist->color.a; -} - -exq_color exq_sort_dir; - -exq_float exq_sort_by_dir(const exq_histogram *pHist) -{ - return pHist->color.r * exq_sort_dir.r + - pHist->color.g * exq_sort_dir.g + - pHist->color.b * exq_sort_dir.b + - pHist->color.a * exq_sort_dir.a; -} diff --git a/tools/n64graphics_ci_dir/exoquant/exoquant.h b/tools/n64graphics_ci_dir/exoquant/exoquant.h deleted file mode 100644 index 6d8224061c..0000000000 --- a/tools/n64graphics_ci_dir/exoquant/exoquant.h +++ /dev/null @@ -1,150 +0,0 @@ -/* -ExoQuant v0.7 - -Copyright (c) 2004 Dennis Ranke - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -/****************************************************************************** -* Usage: -* ------ -* -* exq_data *pExq = exq_init(); // init quantizer (per image) -* exq_feed(pExq, , ); // find palette -* exq_get_palette(pExq, , ); // get palette -* exq_map_image(pExq, , , ); -* or: -* exq_map_image_ordered(pExq, , , , ); -* // map image to palette -* exq_free(pExq); // free memory again -* -* Notes: -* ------ -* -* All 32bpp data (input data and palette data) is considered a byte stream -* of the format: -* R0 G0 B0 A0 R1 G1 B1 A1 ... -* If you want to use a different order, the easiest way to do this is to -* change the SCALE_x constants in expquant.h, as those are the only differences -* between the channels. -* -******************************************************************************/ - -#ifndef __EXOQUANT_H -#define __EXOQUANT_H - -#ifdef __cplusplus -extern "C" { -#endif - -/* type definitions */ -typedef double exq_float; - -typedef struct _exq_color -{ - exq_float r, g, b, a; -} exq_color; - -typedef struct _exq_histogram -{ - exq_color color; - unsigned char ored, ogreen, oblue, oalpha; - int palIndex; - exq_color ditherScale; - int ditherIndex[4]; - int num; - struct _exq_histogram *pNext; - struct _exq_histogram *pNextInHash; -} exq_histogram; - -typedef struct _exq_node -{ - exq_color dir, avg; - exq_float vdif; - exq_float err; - int num; - exq_histogram *pHistogram; - exq_histogram *pSplit; -} exq_node; - -#define EXQ_HASH_BITS 16 -#define EXQ_HASH_SIZE (1 << (EXQ_HASH_BITS)) - -typedef struct _exq_data -{ - exq_histogram *pHash[EXQ_HASH_SIZE]; - exq_node node[256]; - int numColors; - int numBitsPerChannel; - int optimized; - int transparency; -} exq_data; - -/* interface */ - -exq_data *exq_init(); -void exq_no_transparency(exq_data *pExq); -void exq_free(exq_data *pExq); -void exq_feed(exq_data *pExq, unsigned char *pData, - int nPixels); -void exq_quantize(exq_data *pExq, int nColors); -void exq_quantize_hq(exq_data *pExq, int nColors); -void exq_quantize_ex(exq_data *pExq, int nColors, int hq); -exq_float exq_get_mean_error(exq_data *pExq); -void exq_get_palette(exq_data *pExq, unsigned char *pPal, - int nColors); -void exq_set_palette(exq_data *pExq, unsigned char *pPal, - int nColors); -void exq_map_image(exq_data *pExq, int nPixels, - unsigned char *pIn, unsigned char *pOut); -void exq_map_image_ordered(exq_data *pExq, int width, - int height, unsigned char *pIn, - unsigned char *pOut); -void exq_map_image_random(exq_data *pExq, int nPixels, - unsigned char *pIn, unsigned char *pOut); - -/* internal functions */ - -void exq_map_image_dither(exq_data *pExq, int width, - int height, unsigned char *pIn, - unsigned char *pOut, int ordered); - -void exq_sum_node(exq_node *pNode); -void exq_optimize_palette(exq_data *pExp, int iter); - -unsigned char exq_find_nearest_color(exq_data *pExp, exq_color *pColor); -exq_histogram *exq_find_histogram(exq_data *pExp, unsigned char *pCol); - -void exq_sort(exq_histogram **ppHist, - exq_float (*sortfunc)(const exq_histogram *pHist)); -exq_float exq_sort_by_r(const exq_histogram *pHist); -exq_float exq_sort_by_g(const exq_histogram *pHist); -exq_float exq_sort_by_b(const exq_histogram *pHist); -exq_float exq_sort_by_a(const exq_histogram *pHist); -exq_float exq_sort_by_dir(const exq_histogram *pHist); - -extern exq_color exq_sort_dir; - -#ifdef __cplusplus -} -#endif - -#endif // __EXOQUANT_H diff --git a/tools/n64graphics_ci_dir/n64graphics_ci.c b/tools/n64graphics_ci_dir/n64graphics_ci.c deleted file mode 100644 index 6ec4476adb..0000000000 --- a/tools/n64graphics_ci_dir/n64graphics_ci.c +++ /dev/null @@ -1,573 +0,0 @@ -#include -#include -#include - -#define STBI_NO_LINEAR -#define STBI_NO_HDR -#define STBI_NO_TGA -#define STB_IMAGE_IMPLEMENTATION -#include "../stb/stb_image.h" -#define STB_IMAGE_WRITE_IMPLEMENTATION -#include "../stb/stb_image_write.h" - -#include "exoquant/exoquant.h" - -#include "n64graphics_ci.h" -#include "utils.h" - -// SCALE_M_N: upscale/downscale M-bit integer to N-bit -#define SCALE_5_8(VAL_) (((VAL_) * 0xFF) / 0x1F) -#define SCALE_8_5(VAL_) ((((VAL_) + 4) * 0x1F) / 0xFF) -#define SCALE_4_8(VAL_) ((VAL_) * 0x11) -#define SCALE_8_4(VAL_) ((VAL_) / 0x11) -#define SCALE_3_8(VAL_) ((VAL_) * 0x24) -#define SCALE_8_3(VAL_) ((VAL_) / 0x24) - -typedef enum -{ - IMG_FORMAT_CI -} img_format; - -rgba *raw2rgba(const uint8_t *raw, int width, int height, int depth) -{ - rgba *img; - int img_size; - - img_size = width * height * sizeof(*img); - img = malloc(img_size); - if (!img) { - ERROR("Error allocating %d bytes\n", img_size); - return NULL; - } - - if (depth == 16) { - for (int i = 0; i < width * height; i++) { - img[i].red = SCALE_5_8((raw[i * 2] & 0xF8) >> 3); - img[i].green = SCALE_5_8(((raw[i * 2] & 0x07) << 2) | ((raw[i * 2 + 1] & 0xC0) >> 6)); - img[i].blue = SCALE_5_8((raw[i * 2 + 1] & 0x3E) >> 1); - img[i].alpha = (raw[i * 2 + 1] & 0x01) ? 0xFF : 0x00; - } - } - else if (depth == 32) { - for (int i = 0; i < width * height; i++) { - img[i].red = raw[i * 4]; - img[i].green = raw[i * 4 + 1]; - img[i].blue = raw[i * 4 + 2]; - img[i].alpha = raw[i * 4 + 3]; - } - } - - return img; -} - -// extract RGBA from CI raw data and palette -rgba *rawci2rgba(const uint8_t *rawci, const uint8_t *palette, int width, int height, int depth) -{ - uint8_t *raw_rgba; - rgba *img = NULL; - int raw_size; - int pal_index_1; - int pal_index_2; - - // first convert to raw RGBA - raw_size = 2 * width * height; - raw_rgba = malloc(raw_size); - if (!raw_rgba) { - ERROR("Error allocating %u bytes\n", raw_size); - return NULL; - } - - if (depth == 4) { // CI4 - for (int i = 0; i < (width * height) / 2; i++) { - pal_index_1 = rawci[i] >> 4; - pal_index_2 = rawci[i] & 0xF; - raw_rgba[i * 4 + 0] = palette[pal_index_1 * 2 + 0]; - raw_rgba[i * 4 + 1] = palette[pal_index_1 * 2 + 1]; - raw_rgba[i * 4 + 2] = palette[pal_index_2 * 2 + 0]; - raw_rgba[i * 4 + 3] = palette[pal_index_2 * 2 + 1]; - } - } else { // CI8 - for (int i = 0; i < width * height; i++) { - pal_index_1 = rawci[i]; - raw_rgba[i * 2 + 0] = palette[pal_index_1 * 2 + 0]; - raw_rgba[i * 2 + 1] = palette[pal_index_1 * 2 + 1]; - } - } - - // then convert to RGBA image data - img = raw2rgba(raw_rgba, width, height, 16); - - free(raw_rgba); - - return img; -} - -int rgba2rawci(uint8_t *raw, uint8_t *out_palette, int *pal_len, const rgba *img, int width, int height, int depth) -{ - int size = width * height * depth / 8; - int num_colors = *pal_len / 2; - - INFO("Converting RGBA %dx%d to raw CI%d\n", width, height, depth); - - uint8_t *rgba32_palette = malloc(num_colors * 4); - uint8_t *ci8_raw = malloc(width * height); - - // Use ExoQuant to convert the RGBA32 data buffer to an CI8 output - exq_data *pExq = exq_init(); - exq_feed(pExq, (uint8_t*)img, width * height); - exq_quantize_hq(pExq, num_colors); - exq_get_palette(pExq, rgba32_palette, num_colors); - exq_map_image_ordered(pExq, width, height, (uint8_t*)img, ci8_raw); - exq_free(pExq); - - if (depth == 4) { - // Convert CI8 image to CI4 image - for (int i = 0; i < size; i++) { - raw[i] = (ci8_raw[i * 2 + 0] << 4) | ci8_raw[i * 2 + 1]; - } - } - else { - memcpy(raw, ci8_raw, size); - } - - // Convert RGBA32 palette to RGBA16 - for (int i = 0; i < num_colors; i++) { - unsigned char red = (rgba32_palette[i * 4 + 0] / 8) & 0x1F; - unsigned char green = (rgba32_palette[i * 4 + 1] / 8) & 0x1F; - unsigned char blue = (rgba32_palette[i * 4 + 2] / 8) & 0x1F; - unsigned char alpha = rgba32_palette[i * 4 + 3] > 0 ? 1 : 0; // 1 bit alpha - - out_palette[i * 2 + 0] = (red << 3) | (green >> 2); - out_palette[i * 2 + 1] = ((green & 3) << 6) | (blue << 1) | alpha; - } - - free(rgba32_palette); - free(ci8_raw); - return size; -} - -rgba *png2rgba(const char *png_filename, int *width, int *height) -{ - rgba *img = NULL; - int w = 0; - int h = 0; - int channels = 0; - int img_size; - - stbi_uc *data = stbi_load(png_filename, &w, &h, &channels, STBI_default); - if (!data || w <= 0 || h <= 0) { - ERROR("Error loading png file \"%s\"\n", png_filename); - return NULL; - } - INFO("Read \"%s\" %dx%d channels: %d\n", png_filename, w, h, channels); - - img_size = w * h * sizeof(*img); - img = malloc(img_size); - if (!img) { - ERROR("Error allocating %u bytes\n", img_size); - return NULL; - } - - switch (channels) { - case 3: // red, green, blue - case 4: // red, green, blue, alpha - for (int j = 0; j < h; j++) { - for (int i = 0; i < w; i++) { - int idx = j*w + i; - img[idx].red = data[channels*idx]; - img[idx].green = data[channels*idx + 1]; - img[idx].blue = data[channels*idx + 2]; - if (channels == 4) { - img[idx].alpha = data[channels*idx + 3]; - } - else { - img[idx].alpha = 0xFF; - } - } - } - break; - case 2: // grey, alpha - for (int j = 0; j < h; j++) { - for (int i = 0; i < w; i++) { - int idx = j*w + i; - img[idx].red = data[2 * idx]; - img[idx].green = data[2 * idx]; - img[idx].blue = data[2 * idx]; - img[idx].alpha = data[2 * idx + 1]; - } - } - break; - default: - ERROR("Don't know how to read channels: %d\n", channels); - free(img); - img = NULL; - } - - // cleanup - stbi_image_free(data); - - *width = w; - *height = h; - return img; -} - -int rgba2png(const char *png_filename, const rgba *img, int width, int height) -{ - int ret = 0; - INFO("Saving RGBA %dx%d to \"%s\"\n", width, height, png_filename); - - // convert to format stb_image_write expects - uint8_t *data = malloc(4 * width*height); - if (data) { - for (int j = 0; j < height; j++) { - for (int i = 0; i < width; i++) { - int idx = j*width + i; - data[4 * idx] = img[idx].red; - data[4 * idx + 1] = img[idx].green; - data[4 * idx + 2] = img[idx].blue; - data[4 * idx + 3] = img[idx].alpha; - } - } - - ret = stbi_write_png(png_filename, width, height, 4, data, 0); - - free(data); - } - - return ret; -} - -const char *n64graphics_get_read_version(void) -{ - return "stb_image 2.19"; -} - -const char *n64graphics_get_write_version(void) -{ - return "stb_image_write 1.09"; -} - -/***************************************************************/ - -#define N64GRAPHICS_VERSION "0.4 - CI Only Branch" -#include - -typedef enum -{ - MODE_EXPORT, - MODE_IMPORT, -} tool_mode; - -typedef struct -{ - char *img_filename; - char *bin_filename; - tool_mode mode; - unsigned int offset; - img_format format; - int depth; - int width; - int height; - int truncate; -} graphics_config; - -static const graphics_config default_config = -{ - .img_filename = NULL, - .bin_filename = NULL, - .mode = MODE_EXPORT, - .offset = 0, - .format = IMG_FORMAT_CI, - .depth = 8, - .width = 32, - .height = 32, - .truncate = 1, -}; - -typedef struct -{ - const char *name; - img_format format; - int depth; -} format_entry; - -static const format_entry format_table[] = -{ - { "ci4", IMG_FORMAT_CI, 4 }, - { "ci8", IMG_FORMAT_CI, 8 }, -}; - -static const char *format2str(img_format format, int depth) -{ - for (unsigned i = 0; i < DIM(format_table); i++) { - if (format == format_table[i].format && depth == format_table[i].depth) { - return format_table[i].name; - } - } - return "unknown"; -} - -static int parse_format(graphics_config *config, const char *str) -{ - for (unsigned i = 0; i < DIM(format_table); i++) { - if (!strcasecmp(str, format_table[i].name)) { - config->format = format_table[i].format; - config->depth = format_table[i].depth; - return 1; - } - } - return 0; -} - -static void print_usage(void) -{ - ERROR("Usage: n64graphics_ci -e/-i BIN_FILE -g PNG_FILE [-o offset] [-f FORMAT] [-w WIDTH] [-h HEIGHT] [-V]\n" - "\n" - "n64graphics v" N64GRAPHICS_VERSION ": N64 graphics manipulator\n" - "\n" - "Required arguments:\n" - " -e BIN_FILE export from BIN_FILE to PNG_FILE\n" - " -i BIN_FILE import from PNG_FILE to BIN_FILE\n" - " -g PNG_FILE graphics file to import/export (.png)\n" - "Optional arguments:\n" - " -o OFFSET starting offset in BIN_FILE (prevents truncation during import)\n" - " -f FORMAT texture format: ci4, ci8 (default: %s)\n" - " -w WIDTH export texture width (default: %d)\n" - " -h HEIGHT export texture height (default: %d)\n" - " -v verbose logging\n" - " -V print version information\n", - format2str(default_config.format, default_config.depth), - default_config.width, - default_config.height); -} - -static void print_version(void) -{ - ERROR("n64graphics v" N64GRAPHICS_VERSION ", using:\n" - " %s\n" - " %s\n", - n64graphics_get_read_version(), n64graphics_get_write_version()); -} - -// parse command line arguments -static int parse_arguments(int argc, char *argv[], graphics_config *config) -{ - for (int i = 1; i < argc; i++) { - if (argv[i][0] == '-') { - switch (argv[i][1]) { - case 'e': - if (++i >= argc) return 0; - config->bin_filename = argv[i]; - config->mode = MODE_EXPORT; - break; - case 'f': - if (++i >= argc) return 0; - if (!parse_format(config, argv[i])) { - return 0; - } - break; - case 'i': - if (++i >= argc) return 0; - config->bin_filename = argv[i]; - config->mode = MODE_IMPORT; - break; - case 'g': - if (++i >= argc) return 0; - config->img_filename = argv[i]; - break; - case 'h': - if (++i >= argc) return 0; - config->height = strtoul(argv[i], NULL, 0); - break; - case 'o': - if (++i >= argc) return 0; - config->offset = strtoul(argv[i], NULL, 0); - config->truncate = 0; - break; - case 'w': - if (++i >= argc) return 0; - config->width = strtoul(argv[i], NULL, 0); - break; - case 'v': - g_verbosity = 1; - break; - case 'V': - print_version(); - exit(0); - break; - default: - return 0; - break; - } - } - else { - return 0; - } - } - return 1; -} - -char* getPaletteFilename(char* ci_filename) -{ - int bin_filename_len; - int extension_len; - char* pal_bin_filename; - char* extension_loc; - char* extension; - - // Write Palette file - bin_filename_len = strlen(ci_filename); - extension_loc = strrchr(ci_filename, '.'); - extension_len = (int)(extension_loc - ci_filename); - extension = malloc(extension_len); - memcpy(extension, extension_loc, extension_len); - pal_bin_filename = malloc(bin_filename_len + 4); // +4 to include ".pal" - if (!pal_bin_filename) { - ERROR("Error allocating bytes for palette filename\n"); - } - memcpy(pal_bin_filename, ci_filename, bin_filename_len); - strcpy(pal_bin_filename + bin_filename_len, ".pal"); - //strcpy(pal_bin_filename + bin_filename_len, extension); - - free(extension); - - return pal_bin_filename; -} - -int main(int argc, char* argv[]) -{ - graphics_config config = default_config; - rgba *imgr; - FILE *fp; - uint8_t *raw; - int raw_size; - int length = 0; - int flength; - int res; - FILE *fp_pal; // for ci palette - uint8_t *pal; - int pal_len; - char* pal_bin_filename; - - int valid = parse_arguments(argc, argv, &config); - if (!valid || !config.bin_filename || !config.bin_filename || !config.img_filename) { - print_usage(); - exit(EXIT_FAILURE); - } - - if (config.mode == MODE_IMPORT) { - printf("%s\n", config.bin_filename); - if (config.truncate) { - fp = fopen(config.bin_filename, "wb"); - } - else { - fp = fopen(config.bin_filename, "r+b"); - } - if (!fp) { - ERROR("Error opening binary file \"%s\"\n", config.bin_filename); - return -1; - } - if (!config.truncate) { - fseek(fp, config.offset, SEEK_SET); - } - switch (config.format) { - case IMG_FORMAT_CI: - imgr = png2rgba(config.img_filename, &config.width, &config.height); - raw_size = config.width * config.height * config.depth / 8; - raw = malloc(raw_size); - if (!raw) { - ERROR("Error allocating %u bytes\n", raw_size); - } - if (config.depth == 4) { - pal_len = 16 * 2; // CI4 - } - else { - pal_len = 256 * 2; // CI8 - } - pal = malloc(pal_len); - if (!pal) { - ERROR("Error allocating %u bytes for palette\n", pal_len); - } - length = rgba2rawci(raw, pal, &pal_len, imgr, config.width, config.height, config.depth); - //length = rgba2raw(raw, imgr, config.width, config.height, config.depth); - break; - } - if (length <= 0) { - ERROR("Error converting to raw format\n"); - return EXIT_FAILURE; - } - INFO("Writing 0x%X bytes to offset 0x%X of \"%s\"\n", length, config.offset, config.bin_filename); - flength = fwrite(raw, 1, length, fp); - if (flength != length) { - ERROR("Error writing %d bytes to \"%s\"\n", length, config.bin_filename); - } - fclose(fp); - - if (config.format == IMG_FORMAT_CI) { - pal_bin_filename = getPaletteFilename(config.bin_filename); - - fp_pal = fopen(pal_bin_filename, "wb"); - INFO("Writing 0x%X bytes to palette file \"%s\"\n", pal_len, pal_bin_filename); - flength = fwrite(pal, 1, pal_len, fp_pal); - if (flength != pal_len) { - ERROR("Error writing %d bytes to \"%s\"\n", pal_len, pal_bin_filename); - } - fclose(fp_pal); - free(pal_bin_filename); - } - } else { // Export - if (config.width <= 0 || config.height <= 0 || config.depth <= 0) { - ERROR("Error: must set position width and height for export\n"); - return EXIT_FAILURE; - } - fp = fopen(config.bin_filename, "rb"); - if (!fp) { - ERROR("Error opening \"%s\"\n", config.bin_filename); - return -1; - } - raw_size = config.width * config.height * config.depth / 8; - raw = malloc(raw_size); - if (config.offset > 0) { - fseek(fp, config.offset, SEEK_SET); - } - flength = fread(raw, 1, raw_size, fp); - if (flength != raw_size) { - ERROR("Error reading %d bytes from \"%s\"\n", raw_size, config.bin_filename); - } - switch (config.format) { - case IMG_FORMAT_CI: - // Read Palette file - pal_bin_filename = getPaletteFilename(config.bin_filename); - fp_pal = fopen(pal_bin_filename, "rb"); - if (!fp_pal) { - ERROR("Error opening \"%s\"\n", pal_bin_filename); - return -1; - } - if (config.depth == 4) { - pal_len = 16 * 2; // CI4 - } - else { - pal_len = 256 * 2; // CI8 - } - - pal = malloc(pal_len); - if (config.offset > 0) { - fseek(fp, config.offset, SEEK_SET); - } - flength = fread(pal, 1, pal_len, fp_pal); - - imgr = rawci2rgba(raw, pal, config.width, config.height, config.depth); - res = rgba2png(config.img_filename, imgr, config.width, config.height); - - free(pal_bin_filename); - break; - default: - return EXIT_FAILURE; - } - if (!res) { - ERROR("Error writing to \"%s\"\n", config.img_filename); - } - } - - return 0; -} diff --git a/tools/n64graphics_ci_dir/n64graphics_ci.h b/tools/n64graphics_ci_dir/n64graphics_ci.h deleted file mode 100644 index 9142314314..0000000000 --- a/tools/n64graphics_ci_dir/n64graphics_ci.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef N64GRAPHICS_CI_H_ -#define N64GRAPHICS_CI_H_ - -#include - -// intermediate formats -typedef struct _rgba -{ - uint8_t red; - uint8_t green; - uint8_t blue; - uint8_t alpha; -} rgba; - -typedef struct _ia -{ - uint8_t intensity; - uint8_t alpha; -} ia; - -// N64 raw RGBA16/RGBA32 -> intermediate RGBA -rgba *raw2rgba(const uint8_t *raw, int width, int height, int depth); - -// N64 raw CI + palette -> intermediate RGBA -rgba *rawci2rgba(const uint8_t *rawci, const uint8_t *palette, int width, int height, int depth); - -// intermediate RGBA -> N64 raw CI + palette -int rgba2rawci(uint8_t *raw, uint8_t *out_palette, int *pal_len, const rgba *img, int width, int height, int depth); - -// PNG file -> intermediate RGBA -rgba *png2rgba(const char *png_filename, int *width, int *height); - -// intermediate RGBA write to PNG file -int rgba2png(const char *png_filename, const rgba *img, int width, int height); - -// get version of underlying graphics reading library -const char *n64graphics_get_read_version(void); - -// get version of underlying graphics writing library -const char *n64graphics_get_write_version(void); - -#endif diff --git a/tools/n64graphics_ci_dir/utils.c b/tools/n64graphics_ci_dir/utils.c deleted file mode 100644 index 3ba6e3e6f7..0000000000 --- a/tools/n64graphics_ci_dir/utils.c +++ /dev/null @@ -1,276 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#if defined(_MSC_VER) || defined(__MINGW32__) - #include - #include -#else - #include - #include -#endif - -#include "utils.h" - -// global verbosity setting -int g_verbosity = 0; - -int read_s16_be(unsigned char *buf) -{ - unsigned tmp = read_u16_be(buf); - int ret; - if (tmp > 0x7FFF) { - ret = -((int)0x10000 - (int)tmp); - } else { - ret = (int)tmp; - } - return ret; -} - -float read_f32_be(unsigned char *buf) -{ - union {uint32_t i; float f;} ret; - ret.i = read_u32_be(buf); - return ret.f; -} - -int is_power2(unsigned int val) -{ - while (((val & 1) == 0) && (val > 1)) { - val >>= 1; - } - return (val == 1); -} - -void fprint_hex(FILE *fp, const unsigned char *buf, int length) -{ - int i; - for (i = 0; i < length; i++) { - fprint_byte(fp, buf[i]); - fputc(' ', fp); - } -} - -void fprint_hex_source(FILE *fp, const unsigned char *buf, int length) -{ - int i; - for (i = 0; i < length; i++) { - if (i > 0) fputs(", ", fp); - fputs("0x", fp); - fprint_byte(fp, buf[i]); - } -} - -void print_hex(const unsigned char *buf, int length) -{ - fprint_hex(stdout, buf, length); -} - -void swap_bytes(unsigned char *data, long length) -{ - long i; - unsigned char tmp; - for (i = 0; i < length; i += 2) { - tmp = data[i]; - data[i] = data[i+1]; - data[i+1] = tmp; - } -} - -void reverse_endian(unsigned char *data, long length) -{ - long i; - unsigned char tmp; - for (i = 0; i < length; i += 4) { - tmp = data[i]; - data[i] = data[i+3]; - data[i+3] = tmp; - tmp = data[i+1]; - data[i+1] = data[i+2]; - data[i+2] = tmp; - } -} - -long filesize(const char *filename) -{ - struct stat st; - - if (stat(filename, &st) == 0) { - return st.st_size; - } - - return -1; -} - -void touch_file(const char *filename) -{ - int fd; - //fd = open(filename, O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK, 0666); - fd = open(filename, O_WRONLY|O_CREAT, 0666); - if (fd >= 0) { - utime(filename, NULL); - close(fd); - } -} - -long read_file(const char *file_name, unsigned char **data) -{ - FILE *in; - unsigned char *in_buf = NULL; - long file_size; - long bytes_read; - in = fopen(file_name, "rb"); - if (in == NULL) { - return -1; - } - - // allocate buffer to read from offset to end of file - fseek(in, 0, SEEK_END); - file_size = ftell(in); - - // sanity check - if (file_size > 256*MB) { - return -2; - } - - in_buf = malloc(file_size); - fseek(in, 0, SEEK_SET); - - // read bytes - bytes_read = fread(in_buf, 1, file_size, in); - if (bytes_read != file_size) { - return -3; - } - - fclose(in); - *data = in_buf; - return bytes_read; -} - -long write_file(const char *file_name, unsigned char *data, long length) -{ - FILE *out; - long bytes_written; - // open output file - out = fopen(file_name, "wb"); - if (out == NULL) { - perror(file_name); - return -1; - } - bytes_written = fwrite(data, 1, length, out); - fclose(out); - return bytes_written; -} - -void generate_filename(const char *in_name, char *out_name, char *extension) -{ - char tmp_name[FILENAME_MAX]; - int len; - int i; - strcpy(tmp_name, in_name); - len = strlen(tmp_name); - for (i = len - 1; i > 0; i--) { - if (tmp_name[i] == '.') { - break; - } - } - if (i <= 0) { - i = len; - } - tmp_name[i] = '\0'; - sprintf(out_name, "%s.%s", tmp_name, extension); -} - -char *basename(const char *name) -{ - const char *base = name; - while (*name) { - if (*name++ == '/') { - base = name; - } - } - return (char *)base; -} - -void make_dir(const char *dir_name) -{ - struct stat st = {0}; - if (stat(dir_name, &st) == -1) { - mkdir(dir_name, 0755); - } -} - -long copy_file(const char *src_name, const char *dst_name) -{ - unsigned char *buf; - long bytes_written; - long bytes_read; - - bytes_read = read_file(src_name, &buf); - - if (bytes_read > 0) { - bytes_written = write_file(dst_name, buf, bytes_read); - if (bytes_written != bytes_read) { - bytes_read = -1; - } - free(buf); - } - - return bytes_read; -} - -void dir_list_ext(const char *dir, const char *extension, dir_list *list) -{ - char *pool; - char *pool_ptr; - struct dirent *entry; - DIR *dfd; - int idx; - - dfd = opendir(dir); - if (dfd == NULL) { - ERROR("Can't open '%s'\n", dir); - exit(1); - } - - pool = malloc(FILENAME_MAX * MAX_DIR_FILES); - pool_ptr = pool; - - idx = 0; - while ((entry = readdir(dfd)) != NULL && idx < MAX_DIR_FILES) { - if (!extension || str_ends_with(entry->d_name, extension)) { - sprintf(pool_ptr, "%s/%s", dir, entry->d_name); - list->files[idx] = pool_ptr; - pool_ptr += strlen(pool_ptr) + 1; - idx++; - } - } - list->count = idx; - - closedir(dfd); -} - -void dir_list_free(dir_list *list) -{ - // assume first entry in array is allocated - if (list->files[0]) { - free(list->files[0]); - list->files[0] = NULL; - } -} - -int str_ends_with(const char *str, const char *suffix) -{ - if (!str || !suffix) { - return 0; - } - size_t len_str = strlen(str); - size_t len_suffix = strlen(suffix); - if (len_suffix > len_str) { - return 0; - } - return (0 == strncmp(str + len_str - len_suffix, suffix, len_suffix)); -} diff --git a/tools/n64graphics_ci_dir/utils.h b/tools/n64graphics_ci_dir/utils.h deleted file mode 100644 index f038653fe7..0000000000 --- a/tools/n64graphics_ci_dir/utils.h +++ /dev/null @@ -1,153 +0,0 @@ -#ifndef UTILS_H_ -#define UTILS_H_ - -#include - -// defines - -// printing size_t varies by compiler -#if defined(_MSC_VER) || defined(__MINGW32__) - #define SIZE_T_FORMAT "%Iu" -#else - #define SIZE_T_FORMAT "%zu" -#endif - -#define KB 1024 -#define MB (1024 * KB) - -// number of elements in statically declared array -#define DIM(S_ARR_) (sizeof(S_ARR_) / sizeof(S_ARR_[0])) - -#define MIN(A_, B_) ((A_) < (B_) ? (A_) : (B_)) -#define MAX(A_, B_) ((A_) > (B_) ? (A_) : (B_)) - -// align value to N-byte boundary -#define ALIGN(VAL_, ALIGNMENT_) (((VAL_) + ((ALIGNMENT_) - 1)) & ~((ALIGNMENT_) - 1)) - -// read/write u32/16 big/little endian -#define read_u32_be(buf) (unsigned int)(((buf)[0] << 24) + ((buf)[1] << 16) + ((buf)[2] << 8) + ((buf)[3])) -#define read_u32_le(buf) (unsigned int)(((buf)[1] << 24) + ((buf)[0] << 16) + ((buf)[3] << 8) + ((buf)[2])) -#define write_u32_be(buf, val) do { \ - (buf)[0] = ((val) >> 24) & 0xFF; \ - (buf)[1] = ((val) >> 16) & 0xFF; \ - (buf)[2] = ((val) >> 8) & 0xFF; \ - (buf)[3] = (val) & 0xFF; \ -} while(0) -#define read_u16_be(buf) (((buf)[0] << 8) + ((buf)[1])) -#define write_u16_be(buf, val) do { \ - (buf)[0] = ((val) >> 8) & 0xFF; \ - (buf)[1] = ((val)) & 0xFF; \ -} while(0) - -// print nibbles and bytes -#define fprint_nibble(FP, NIB_) fputc((NIB_) < 10 ? ('0' + (NIB_)) : ('A' + (NIB_) - 0xA), FP) -#define fprint_byte(FP, BYTE_) do { \ - fprint_nibble(FP, (BYTE_) >> 4); \ - fprint_nibble(FP, (BYTE_) & 0x0F); \ - } while(0) -#define print_nibble(NIB_) fprint_nibble(stdout, NIB_) -#define print_byte(BYTE_) fprint_byte(stdout, BYTE_) - -// Windows compatibility -#if defined(_MSC_VER) || defined(__MINGW32__) - #include - #define mkdir(DIR_, PERM_) _mkdir(DIR_) - #ifndef strcasecmp - #define strcasecmp(A, B) stricmp(A, B) - #endif -#endif - -// typedefs - -#define MAX_DIR_FILES 128 -typedef struct -{ - char *files[MAX_DIR_FILES]; - int count; -} dir_list; - -// global verbosity setting -extern int g_verbosity; - -#define ERROR(...) fprintf(stderr, __VA_ARGS__) -#define INFO(...) if (g_verbosity) printf(__VA_ARGS__) -#define INFO_HEX(...) if (g_verbosity) print_hex(__VA_ARGS__) - -// functions - -// convert two bytes in big-endian to signed int -int read_s16_be(unsigned char *buf); - -// convert four bytes in big-endian to float -float read_f32_be(unsigned char *buf); - -// determine if value is power of 2 -// returns 1 if val is power of 2, 0 otherwise -int is_power2(unsigned int val); - -// print buffer as hex bytes -// fp: file pointer -// buf: buffer to read bytes from -// length: length of buffer to print -void fprint_hex(FILE *fp, const unsigned char *buf, int length); -void fprint_hex_source(FILE *fp, const unsigned char *buf, int length); -void print_hex(const unsigned char *buf, int length); - -// perform byteswapping to convert from v64 to z64 ordering -void swap_bytes(unsigned char *data, long length); - -// reverse endian to convert from n64 to z64 ordering -void reverse_endian(unsigned char *data, long length); - -// get size of file without opening it; -// returns file size or negative on error -long filesize(const char *file_name); - -// update file timestamp to now, creating it if it doesn't exist -void touch_file(const char *filename); - -// read entire contents of file into buffer -// returns file size or negative on error -long read_file(const char *file_name, unsigned char **data); - -// write buffer to file -// returns number of bytes written out or -1 on failure -long write_file(const char *file_name, unsigned char *data, long length); - -// generate an output file name from input name by replacing file extension -// in_name: input file name -// out_name: buffer to write output name in -// extension: new file extension to use -void generate_filename(const char *in_name, char *out_name, char *extension); - -// extract base filename from file path -// name: path to file -// returns just the file name after the last '/' -char *basename(const char *name); - -// make a directory if it doesn't exist -// dir_name: name of the directory -void make_dir(const char *dir_name); - -// copy a file from src_name to dst_name. will not make directories -// src_name: source file name -// dst_name: destination file name -long copy_file(const char *src_name, const char *dst_name); - -// list a directory, optionally filtering files by extension -// dir: directory to list files in -// extension: extension to filter files by (NULL if no filtering) -// list: output list and count -void dir_list_ext(const char *dir, const char *extension, dir_list *list); - -// free associated date from a directory list -// list: directory list filled in by dir_list_ext() call -void dir_list_free(dir_list *list); - -// determine if a string ends with another string -// str: string to check if ends with 'suffix' -// suffix: string to see if 'str' ends with -// returns 1 if 'str' ends with 'suffix' -int str_ends_with(const char *str, const char *suffix); - -#endif // UTILS_H_