diff --git a/project/msvc/grafx2-win32.vcxproj b/project/msvc/grafx2-win32.vcxproj
index fa2d05e5..697a798e 100644
--- a/project/msvc/grafx2-win32.vcxproj
+++ b/project/msvc/grafx2-win32.vcxproj
@@ -82,6 +82,7 @@
+
CPU_6502_STATIC;CPU_6502_USE_LOCAL_HEADER;CPU_6502_DEPENDENCIES_H="6502types.h";%(PreprocessorDefinitions)
CPU_6502_STATIC;CPU_6502_USE_LOCAL_HEADER;CPU_6502_DEPENDENCIES_H="6502types.h";%(PreprocessorDefinitions)
diff --git a/project/msvc/grafx2-win32.vcxproj.filters b/project/msvc/grafx2-win32.vcxproj.filters
index 5c2886ae..1c1da724 100644
--- a/project/msvc/grafx2-win32.vcxproj.filters
+++ b/project/msvc/grafx2-win32.vcxproj.filters
@@ -234,6 +234,9 @@
Fichiers sources
+
+ Fichiers sources
+
diff --git a/src/2gsformats.c b/src/2gsformats.c
new file mode 100644
index 00000000..6c195ea8
--- /dev/null
+++ b/src/2gsformats.c
@@ -0,0 +1,302 @@
+/* vim:expandtab:ts=2 sw=2:
+*/
+/* Grafx2 - The Ultimate 256-color bitmap paint program
+
+ Copyright owned by various GrafX2 authors, see COPYRIGHT.txt for details.
+
+ Grafx2 is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; version 2
+ of the License.
+
+ Grafx2 is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Grafx2; if not, see
+*/
+
+///@file 2gsformats.c
+/// Formats for the Apple II GS
+
+#include
+#include
+#include
+#include "global.h"
+#include "fileformats.h"
+#include "loadsavefuncs.h"
+#include "io.h"
+#include "gfx2mem.h"
+#include "gfx2log.h"
+
+/**
+ * Test for an Apple 2 GS picture file
+ */
+void Test_2GS(T_IO_Context * context, FILE * file)
+{
+ unsigned long file_size;
+ dword chunksize;
+ byte header[5];
+
+ (void)context;
+ File_error = 1;
+ file_size = File_length_file(file);
+ if (!Read_dword_le(file, &chunksize))
+ return;
+ if (!Read_bytes(file, header, sizeof(header)))
+ return;
+ if (0 != memcmp(header, "\x04MAIN", 5))
+ return;
+ if (file_size < chunksize)
+ return;
+ File_error = 0;
+}
+
+/**
+ * Load a 16 entry Apple II GS palette from file
+ */
+static int Load_2GS_Palette(T_Components * palette, FILE * file)
+{
+ unsigned int i;
+ for (i = 0; i < 16; i++)
+ {
+ word palentry;
+ if (!Read_word_le(file, &palentry))
+ return 0;
+ palette[i].R = ((palentry >> 8) & 0x0f) * 0x11;
+ palette[i].G = ((palentry >> 4) & 0x0f) * 0x11;
+ palette[i].B = (palentry & 0x0f) * 0x11;
+ }
+ return 1;
+}
+
+#define COMPONENTS(x) linepal[x].R, linepal[x].G, linepal[x].B
+
+/**
+ * set the (2 or 4) pixels for the byte.
+ */
+#define WRITE2GS_PIXELS \
+if (multipalcount > 0) \
+{ \
+ if (linemode & 0x80) \
+ { \
+ Set_pixel_24b(context, x++, y, COMPONENTS(8 + (pixel >> 6))); \
+ Set_pixel_24b(context, x++, y, COMPONENTS(12 + ((pixel >> 4) & 3))); \
+ Set_pixel_24b(context, x++, y, COMPONENTS((pixel >> 2) & 3)); \
+ Set_pixel_24b(context, x++, y, COMPONENTS(4 + (pixel & 3))); \
+ } \
+ else \
+ { \
+ Set_pixel_24b(context, x++, y, COMPONENTS(pixel >> 4)); \
+ Set_pixel_24b(context, x++, y, COMPONENTS(pixel & 15)); \
+ } \
+} \
+else \
+{ \
+ if (linemode & 0x80) \
+ { \
+ Set_pixel(context, x++, y, (linemode << 4) | 8 | (pixel >> 6)); \
+ Set_pixel(context, x++, y, (linemode << 4) | 12 | ((pixel >> 4) & 3)); \
+ Set_pixel(context, x++, y, (linemode << 4) | ((pixel >> 2) & 3)); \
+ Set_pixel(context, x++, y, (linemode << 4) | 4 | (pixel & 3)); \
+ } \
+ else \
+ { \
+ Set_pixel(context, x++, y, (linemode << 4) | (pixel >> 4)); \
+ Set_pixel(context, x++, y, (linemode << 4) | (pixel & 15)); \
+ } \
+}
+
+void Load_2GS(T_IO_Context * context)
+{
+ FILE * file;
+ unsigned long file_size;
+ dword chunksize;
+ byte header[5];
+ word mode;
+ word width;
+ word height;
+ word palcount;
+ word palindex;
+ long lineoffset;
+ long dataoffset;
+ word y;
+ byte bpp = 2;
+ word multipalcount = 0;
+ long multipaloffset = -1;
+ long offset;
+
+ File_error = 1;
+ file = Open_file_read(context);
+ if (file == NULL)
+ return;
+ file_size = File_length_file(file);
+ if (!Read_dword_le(file, &chunksize))
+ return;
+ if (!Read_bytes(file, header, sizeof(header)))
+ goto error;
+ if (0 != memcmp(header, "\x04MAIN", 5))
+ goto error;
+ if (!Read_word_le(file, &mode) || !Read_word_le(file, &width) || !Read_word_le(file, &palcount))
+ goto error;
+
+ if (Config.Clear_palette)
+ memset(context->Palette, 0, sizeof(T_Palette));
+ for (palindex = 0; palindex < palcount; palindex++)
+ {
+ if (!Load_2GS_Palette(context->Palette + (palindex * 16), file))
+ goto error;
+ }
+ if (!Read_word_le(file, &height))
+ goto error;
+ lineoffset = ftell(file);
+ dataoffset = lineoffset + 4 * (long)height;
+ GFX2_Log(GFX2_DEBUG, " mode %02x %ux%u %u palettes\n", mode, (unsigned)width, (unsigned)height, (unsigned)palcount);
+
+ // read other chunks before decoding the picture
+ GFX2_Log(GFX2_DEBUG, "file_size : %06lx, chunksize : %06lx, current offset : %06lx\n", file_size, chunksize, dataoffset);
+ offset = chunksize;
+ while (offset < (long)file_size)
+ {
+ char * p;
+ byte c;
+ fseek(file, offset, SEEK_SET);
+ if (!Read_dword_le(file, &chunksize) || !Read_byte(file, &c))
+ goto error;
+ p = GFX2_malloc(c + 1);
+ if (p)
+ {
+ fread(p, 1, c, file);
+ p[c] = '\0';
+ GFX2_Log(GFX2_DEBUG, "%06lx: %04x %s\n", offset, chunksize, p);
+ if (c == 4 && 0 == memcmp(p, "NOTE", 4))
+ {
+ word len;
+ if (Read_word_le(file, &len))
+ {
+ free(p);
+ p = GFX2_malloc(len + 1);
+ if (p)
+ {
+ fread(p, 1, len, file);
+ p[len] = '\0';
+ GFX2_Log(GFX2_DEBUG, "%s\n", p);
+ }
+ }
+ }
+ else if (c == 12 && 0 == memcmp(p, "SuperConvert", 12))
+ {
+ dword len = chunksize - 4 - 13;
+ free(p);
+ p = GFX2_malloc(len);
+ if (p)
+ {
+ fread(p, 1, len, file);
+ GFX2_LogHexDump(GFX2_DEBUG, "", p, 0 /*offset + 4 + 13*/, (long)len);
+ }
+ }
+ else if (c == 8 && 0 == memcmp(p, "MULTIPAL", 8))
+ {
+ if (Read_word_le(file, &multipalcount))
+ {
+ // all palettes are there...
+ multipaloffset = ftell(file);
+ }
+ }
+ free(p);
+ }
+ offset += chunksize;
+ }
+
+ if (multipalcount > 0)
+ {
+ bpp = 12;
+ }
+ else
+ {
+ while ((1 << bpp) < ((mode & 0x80) ? 4 : 16) * palcount)
+ bpp++;
+ }
+ File_error = 0;
+ Pre_load(context, width, height, file_size, FORMAT_2GS, (mode & 0x80) ? PIXEL_TALL : PIXEL_SIMPLE, bpp);
+ if (File_error)
+ goto error;
+ for (y = 0; y < height; y++)
+ {
+ word linebytes;
+ byte linemode;
+ word x;
+ T_Components linepal[16];
+ if (multipalcount > y)
+ {
+ fseek(file, multipaloffset + 32 * y, SEEK_SET);
+ if (!Load_2GS_Palette(linepal, file))
+ goto error;
+ }
+ fseek(file, lineoffset, SEEK_SET);
+ if (!Read_word_le(file, &linebytes) || !Read_byte(file, &linemode))
+ goto error;
+ //GFX2_Log(GFX2_DEBUG, "Line #%03u mode/pal=%02x %u bytes\n", (unsigned)y, linemode, (unsigned)linebytes);
+ lineoffset += 4;
+ fseek(file, dataoffset, SEEK_SET);
+ if (multipalcount > 0)
+ {
+ linemode = 0;
+ }
+ for (x = 0; x < width; )
+ {
+ byte code, pixel;
+ unsigned count;
+ if (!Read_byte(file, &code))
+ goto error;
+ count = (code & 0x3f) + 1;
+ switch (code >> 6)
+ {
+ case 0: // unpacked
+ while (count-- > 0)
+ {
+ if (!Read_byte(file, &pixel))
+ goto error;
+ WRITE2GS_PIXELS
+ }
+ break;
+ case 3: // byte packed x 4
+ count <<= 2;
+ case 1: // byte packed
+ if (!Read_byte(file, &pixel))
+ goto error;
+ while (count-- > 0)
+ {
+ WRITE2GS_PIXELS
+ }
+ break;
+ case 2: // dword packed
+ {
+ byte pixels[4];
+ if (!Read_bytes(file, pixels, 4))
+ goto error;
+ while (count-- > 0)
+ {
+ int i;
+ for (i = 0; i < 4; i++)
+ {
+ pixel = pixels[i];
+ WRITE2GS_PIXELS
+ }
+ }
+ }
+ break;
+ }
+ }
+ dataoffset += linebytes;
+ }
+
+ fclose(file);
+ return;
+
+error:
+ File_error = 1;
+ fclose(file);
+}
\ No newline at end of file
diff --git a/src/Makefile b/src/Makefile
index e3299f2b..c108d275 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -819,6 +819,7 @@ OBJS = main.o init.o graph.o $(APIOBJ) misc.o osdep.o special.o \
loadsave.o loadsavefuncs.o \
pngformat.o motoformats.o stformats.o c64formats.o cpcformats.o \
ifformat.o msxformats.o packbits.o giformat.o \
+ 2gsformats.o \
fileformats.o miscfileformats.o libraw2crtc.o \
brush_ops.o buttons_effects.o layers.o \
oldies.o tiles.o colorred.o unicode.o gfx2surface.o \
diff --git a/src/const.h b/src/const.h
index b865b1a4..3afc0b81 100644
--- a/src/const.h
+++ b/src/const.h
@@ -136,6 +136,7 @@ enum FILE_FORMATS
FORMAT_FLI, ///< Autodesk Animator FLI/FLC
FORMAT_MOTO, ///< Thomson MO/TO computers pictures
FORMAT_HGR, ///< Apple II HGR and DHGR
+ FORMAT_2GS, ///< Apple II GS format
FORMAT_TIFF, ///< Tagged Image File Format
FORMAT_GRB, ///< HP-48 Grob
FORMAT_MSX, ///< MSX formats
diff --git a/src/fileformats.h b/src/fileformats.h
index 7ca44330..c6ff08a6 100644
--- a/src/fileformats.h
+++ b/src/fileformats.h
@@ -207,5 +207,10 @@ void Test_MSX(T_IO_Context *, FILE *);
void Load_MSX(T_IO_Context *);
void Save_MSX(T_IO_Context *);
+// -- Apple II GS -----------------------------------------------------------
+void Test_2GS(T_IO_Context *, FILE *);
+void Load_2GS(T_IO_Context *);
+void Save_2GS(T_IO_Context *);
+
/// @}
#endif
diff --git a/src/loadsave.c b/src/loadsave.c
index ae0000b0..5aee3b65 100644
--- a/src/loadsave.c
+++ b/src/loadsave.c
@@ -114,6 +114,7 @@ const T_Format File_formats[] = {
"gpx;"
"cpc;scr;win;pph;cm5;go1;sgx;"
"hgr;dhgr;"
+ "shr;gs;iigs;32k;"
"grb;grob;"
"sc2;"
"tga;pnm;xpm;xcf;jpg;jpeg;tif;tiff;ico;ic2;cur;info;flc;bin;map"},
@@ -156,6 +157,7 @@ const T_Format File_formats[] = {
{FORMAT_MOTO," moto",Test_MOTO,Load_MOTO,Save_MOTO,0, 1, 0, "bin", "bin;map"},
{FORMAT_MSX, " msx", Test_MSX, Load_MSX, Save_MSX, 0, 0, 0, "sc2", "sc2"},
{FORMAT_HGR, " hgr", Test_HGR, Load_HGR, Save_HGR, 0, 0, 1, "hgr", "hgr;dhgr;bin"},
+ {FORMAT_2GS, " 2gs", Test_2GS, Load_2GS, NULL, 0, 0, 0, "shr", "shr;gs;iigs;32k"},
#ifndef __no_tifflib__
{FORMAT_TIFF," tiff",Test_TIFF,Load_TIFF,Save_TIFF,0, 1, 1, "tif", "tif;tiff"},
#endif