├── LICENSE
├── Makefile
├── README.md
└── src
└── main.c
/LICENSE:
--------------------------------------------------------------------------------
1 | CC0 1.0 Universal
2 |
3 | Statement of Purpose
4 |
5 | The laws of most jurisdictions throughout the world automatically confer
6 | exclusive Copyright and Related Rights (defined below) upon the creator and
7 | subsequent owner(s) (each and all, an "owner") of an original work of
8 | authorship and/or a database (each, a "Work").
9 |
10 | Certain owners wish to permanently relinquish those rights to a Work for the
11 | purpose of contributing to a commons of creative, cultural and scientific
12 | works ("Commons") that the public can reliably and without fear of later
13 | claims of infringement build upon, modify, incorporate in other works, reuse
14 | and redistribute as freely as possible in any form whatsoever and for any
15 | purposes, including without limitation commercial purposes. These owners may
16 | contribute to the Commons to promote the ideal of a free culture and the
17 | further production of creative, cultural and scientific works, or to gain
18 | reputation or greater distribution for their Work in part through the use and
19 | efforts of others.
20 |
21 | For these and/or other purposes and motivations, and without any expectation
22 | of additional consideration or compensation, the person associating CC0 with a
23 | Work (the "Affirmer"), to the extent that he or she is an owner of Copyright
24 | and Related Rights in the Work, voluntarily elects to apply CC0 to the Work
25 | and publicly distribute the Work under its terms, with knowledge of his or her
26 | Copyright and Related Rights in the Work and the meaning and intended legal
27 | effect of CC0 on those rights.
28 |
29 | 1. Copyright and Related Rights. A Work made available under CC0 may be
30 | protected by copyright and related or neighboring rights ("Copyright and
31 | Related Rights"). Copyright and Related Rights include, but are not limited
32 | to, the following:
33 |
34 | i. the right to reproduce, adapt, distribute, perform, display, communicate,
35 | and translate a Work;
36 |
37 | ii. moral rights retained by the original author(s) and/or performer(s);
38 |
39 | iii. publicity and privacy rights pertaining to a person's image or likeness
40 | depicted in a Work;
41 |
42 | iv. rights protecting against unfair competition in regards to a Work,
43 | subject to the limitations in paragraph 4(a), below;
44 |
45 | v. rights protecting the extraction, dissemination, use and reuse of data in
46 | a Work;
47 |
48 | vi. database rights (such as those arising under Directive 96/9/EC of the
49 | European Parliament and of the Council of 11 March 1996 on the legal
50 | protection of databases, and under any national implementation thereof,
51 | including any amended or successor version of such directive); and
52 |
53 | vii. other similar, equivalent or corresponding rights throughout the world
54 | based on applicable law or treaty, and any national implementations thereof.
55 |
56 | 2. Waiver. To the greatest extent permitted by, but not in contravention of,
57 | applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and
58 | unconditionally waives, abandons, and surrenders all of Affirmer's Copyright
59 | and Related Rights and associated claims and causes of action, whether now
60 | known or unknown (including existing as well as future claims and causes of
61 | action), in the Work (i) in all territories worldwide, (ii) for the maximum
62 | duration provided by applicable law or treaty (including future time
63 | extensions), (iii) in any current or future medium and for any number of
64 | copies, and (iv) for any purpose whatsoever, including without limitation
65 | commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes
66 | the Waiver for the benefit of each member of the public at large and to the
67 | detriment of Affirmer's heirs and successors, fully intending that such Waiver
68 | shall not be subject to revocation, rescission, cancellation, termination, or
69 | any other legal or equitable action to disrupt the quiet enjoyment of the Work
70 | by the public as contemplated by Affirmer's express Statement of Purpose.
71 |
72 | 3. Public License Fallback. Should any part of the Waiver for any reason be
73 | judged legally invalid or ineffective under applicable law, then the Waiver
74 | shall be preserved to the maximum extent permitted taking into account
75 | Affirmer's express Statement of Purpose. In addition, to the extent the Waiver
76 | is so judged Affirmer hereby grants to each affected person a royalty-free,
77 | non transferable, non sublicensable, non exclusive, irrevocable and
78 | unconditional license to exercise Affirmer's Copyright and Related Rights in
79 | the Work (i) in all territories worldwide, (ii) for the maximum duration
80 | provided by applicable law or treaty (including future time extensions), (iii)
81 | in any current or future medium and for any number of copies, and (iv) for any
82 | purpose whatsoever, including without limitation commercial, advertising or
83 | promotional purposes (the "License"). The License shall be deemed effective as
84 | of the date CC0 was applied by Affirmer to the Work. Should any part of the
85 | License for any reason be judged legally invalid or ineffective under
86 | applicable law, such partial invalidity or ineffectiveness shall not
87 | invalidate the remainder of the License, and in such case Affirmer hereby
88 | affirms that he or she will not (i) exercise any of his or her remaining
89 | Copyright and Related Rights in the Work or (ii) assert any associated claims
90 | and causes of action with respect to the Work, in either case contrary to
91 | Affirmer's express Statement of Purpose.
92 |
93 | 4. Limitations and Disclaimers.
94 |
95 | a. No trademark or patent rights held by Affirmer are waived, abandoned,
96 | surrendered, licensed or otherwise affected by this document.
97 |
98 | b. Affirmer offers the Work as-is and makes no representations or warranties
99 | of any kind concerning the Work, express, implied, statutory or otherwise,
100 | including without limitation warranties of title, merchantability, fitness
101 | for a particular purpose, non infringement, or the absence of latent or
102 | other defects, accuracy, or the present or absence of errors, whether or not
103 | discoverable, all to the greatest extent permissible under applicable law.
104 |
105 | c. Affirmer disclaims responsibility for clearing rights of other persons
106 | that may apply to the Work or any use thereof, including without limitation
107 | any person's Copyright and Related Rights in the Work. Further, Affirmer
108 | disclaims responsibility for obtaining any necessary consents, permissions
109 | or other rights required for any use of the Work.
110 |
111 | d. Affirmer understands and acknowledges that Creative Commons is not a
112 | party to this document and has no duty or obligation with respect to this
113 | CC0 or use of the Work.
114 |
115 | For more information, please see
116 |
117 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | OBJDIR=obj
2 | SRCDIR=src
3 |
4 | CC=gcc
5 | LD=ld
6 | CFLAGS=-std=c11
7 | LDSTATIC = $(shell libpng-config --ldflags )
8 |
9 | _OBJS = main.o
10 |
11 | ifdef RELEASE
12 | DEFS += -xSSE3 -O3 -DNDEBUG
13 | else
14 | DEFS += -g
15 | endif
16 |
17 | OBJS = $(patsubst %,$(OBJDIR)/%,$(_OBJS))
18 |
19 | p8png: $(OBJS)
20 | $(CC) -o $@ $^ $(LDSTATIC)
21 |
22 | $(OBJDIR)/%.o: $(SRCDIR)/%.c $(OBJDIR)
23 | $(CC) -c -o $@ $< $(CFLAGS) $(DEFS)
24 |
25 | $(OBJDIR):
26 | mkdir $(OBJDIR)
27 |
28 | clean:
29 | rm -rf $(OBJDIR)/*.o p8png
30 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # pico8-customcart
2 | Quick and dirty custom PICO-8 cartridge PNG generator
3 |
4 | # What is it?
5 | This tool allows the user to combine a PICO-8 cartridge (in .p8.png format) and a custom picture (160x205 px) in order to create an image that loads perfectly in PICO-8 while looking like the picture.
6 |
7 | # What is that PICO-8 you keep talking about?
8 | [PICO-8](http://www.lexaloffle.com/pico-8.php) is a game-making tool styled as a "fantasy console". It has a built in code, graphics and sound editor, allowing anyone to create their own games or edit others'. The games are distributed on the offical website's BBS as ".p8.png" files -- pictures with the game's data encoded inside them.
9 |
10 | # How does the tool work?
11 | Basically, it extracts the lowest two color/alpha bits from every pixel of the "data image" (the original PICO-8 cart) -- that's where PICO-8 hides the actual cartridge dats -- to replace the lowest two color/alpha bits in the "picture image" (the custom label).
12 |
--------------------------------------------------------------------------------
/src/main.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 |
6 | int main (int argc, char** argv) {
7 |
8 | if (argc != 4) { printf("Usage: %s [dfile.p8.png] [ifile.p8.png] [ofile.p8.png]\n",argv[0]); return 1;}
9 |
10 | const char* dfname = argv[1];
11 | const char* pfname = argv[2];
12 | const char* ofname = argv[3];
13 |
14 | FILE* dfile = fopen(dfname,"rb");
15 |
16 | if (!dfile) {
17 | perror("fopen data file"); return 1; }
18 |
19 | FILE* pfile = fopen(pfname,"rb");
20 |
21 | if (!pfile) {
22 | perror("fopen picture file"); return 1; }
23 |
24 | char pnghead[8];
25 |
26 | if (fread(pnghead,1,8,dfile) != 8) {
27 | perror("read data"); return 1;}
28 |
29 | if (png_sig_cmp(pnghead,0,8) != 0) {
30 | fprintf(stderr,"data file is not a PNG!\n"); return 1;}
31 |
32 | png_structp d_png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
33 | if (!d_png) {fprintf(stderr,"Can't create png_structp for data img\n"); return 1;}
34 |
35 | png_infop d_inf = png_create_info_struct(d_png);
36 |
37 | png_init_io(d_png, dfile);
38 | png_set_sig_bytes(d_png, 8);
39 |
40 | png_read_info(d_png, d_inf);
41 |
42 | uint32_t d_w = png_get_image_width(d_png, d_inf);
43 | uint32_t d_h = png_get_image_height(d_png, d_inf);
44 |
45 | if ((d_w != 160) || (d_h != 205)) {
46 | fprintf(stderr,"Data image doesn't match PICO-8's dimensions.\n"
47 | "PICO-8 cartridge PNGs have a size of 160x205.\n");
48 | return 1;}
49 |
50 | int d_col = png_get_color_type(d_png, d_inf);
51 | if (d_col != PNG_COLOR_TYPE_RGB_ALPHA) {
52 | fprintf(stderr,"Data image should be 8-bit RGBA.\n"); return 1; }
53 |
54 |
55 | int d_passes = png_set_interlace_handling(d_png);
56 | png_read_update_info(d_png,d_inf);
57 |
58 | png_bytep* d_rowptrs = malloc(sizeof(png_bytep) * d_h);
59 | for (int y=0; y < d_h; y++) d_rowptrs[y] = malloc(png_get_rowbytes(d_png, d_inf));
60 |
61 | png_read_image(d_png, d_rowptrs);
62 | fclose(dfile);
63 |
64 | if (fread(pnghead,1,8,pfile) != 8) {
65 | perror("read picture"); return 1;}
66 |
67 | if (png_sig_cmp(pnghead,0,8) != 0) {
68 | fprintf(stderr,"picture file is not a PNG!\n"); return 1;}
69 |
70 | png_structp p_png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
71 | if (!p_png) {fprintf(stderr,"Can't create png_structp for picture img\n"); return 1;}
72 |
73 | png_infop p_inf = png_create_info_struct(p_png);
74 |
75 | png_init_io(p_png, pfile);
76 | png_set_sig_bytes(p_png, 8);
77 |
78 | png_read_info(p_png, p_inf);
79 |
80 | uint32_t p_w = png_get_image_width(p_png, p_inf);
81 | uint32_t p_h = png_get_image_height(p_png, p_inf);
82 |
83 | if ((p_w != 160) || (p_h != 205)) {
84 | fprintf(stderr,"Picture image doesn't match PICO-8's dimensions.\n"
85 | "PICO-8 cartridge PNGs have a size of 160x205.\n");
86 | return 1;}
87 |
88 | int p_col = png_get_color_type(p_png, p_inf);
89 |
90 | if (p_col != PNG_COLOR_TYPE_RGB_ALPHA)
91 | printf("Picture PNG is not RGBA. Trying to convert...\n");
92 |
93 | switch(p_col) {
94 |
95 | case PNG_COLOR_TYPE_PALETTE:
96 | png_set_palette_to_rgb(p_png);
97 | if (png_get_valid(p_png,p_inf,PNG_INFO_tRNS))
98 | png_set_tRNS_to_alpha(p_png);
99 | png_set_add_alpha(p_png, 0xFF, PNG_FILLER_AFTER);
100 | break;
101 | case PNG_COLOR_TYPE_GRAY:
102 | png_set_gray_to_rgb(p_png);
103 | png_set_add_alpha(p_png, 0xFF, PNG_FILLER_AFTER);
104 | break;
105 | case PNG_COLOR_TYPE_GRAY_ALPHA:
106 | png_set_gray_to_rgb(p_png);
107 | break;
108 | case PNG_COLOR_TYPE_RGB:
109 | png_set_add_alpha(p_png, 0xFF, PNG_FILLER_AFTER);
110 | break;
111 | }
112 |
113 | int p_passes = png_set_interlace_handling(p_png);
114 | png_read_update_info(p_png,p_inf);
115 |
116 | png_bytep* p_rowptrs = malloc(sizeof(png_bytep) * p_h);
117 | for (int y=0; y < p_h; y++) p_rowptrs[y] = malloc(png_get_rowbytes(p_png, p_inf));
118 |
119 | png_read_image(p_png, p_rowptrs);
120 | fclose(pfile);
121 |
122 | // ---- output file
123 |
124 | FILE* ofile = fopen(ofname,"wb");
125 | if (!ofile) {
126 | perror("fopen output file"); return 1; }
127 |
128 | png_structp o_png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
129 | if (!o_png) {fprintf(stderr,"Can't create png_structp for output img\n"); return 1;}
130 |
131 | png_infop o_inf = png_create_info_struct(o_png);
132 | if (!o_inf) {fprintf(stderr,"Can't create png_infop for output img\n"); return 1;}
133 |
134 | png_init_io(o_png,ofile);
135 |
136 | png_set_IHDR(o_png, o_inf, 160, 205, 8, PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
137 | png_write_info(o_png, o_inf);
138 |
139 | for (int iy=0; iy < 205; iy++) {
140 | for (int ix=0; ix < 160; ix++) {
141 | p_rowptrs[iy][ix*4 ] = ((p_rowptrs[iy][ix*4 ] & 0xFC) | (d_rowptrs[iy][ix*4 ] & 0x03));
142 | p_rowptrs[iy][ix*4+1] = ((p_rowptrs[iy][ix*4+1] & 0xFC) | (d_rowptrs[iy][ix*4+1] & 0x03));
143 | p_rowptrs[iy][ix*4+2] = ((p_rowptrs[iy][ix*4+2] & 0xFC) | (d_rowptrs[iy][ix*4+2] & 0x03));
144 | p_rowptrs[iy][ix*4+3] = ((p_rowptrs[iy][ix*4+3] & 0xFC) | (d_rowptrs[iy][ix*4+3] & 0x03));
145 | }
146 | }
147 |
148 | png_write_image(o_png, p_rowptrs);
149 | png_write_end(o_png, NULL);
150 |
151 | for (int y=0; y < p_h; y++) {
152 | free(d_rowptrs[y]);
153 | free(p_rowptrs[y]); }
154 |
155 | free(d_rowptrs); free(p_rowptrs);
156 |
157 | fclose(ofile);
158 |
159 | }
160 |
--------------------------------------------------------------------------------