    inline void strunescape(char *const str) {
#define cimg_strunescape(ci,co) case ci : *nd = co; ++ns; break;

      unsigned char val = 0;
      for (char *ns = str, *nd = str; *ns || (bool)(*nd = 0); ++nd) if (*ns=='\\') switch (*(++ns)) {
            cimg_strunescape('a','\a');
            cimg_strunescape('b','\b');
            cimg_strunescape('e',0x1B);
            cimg_strunescape('f','\f');
            cimg_strunescape('n','\n');
            cimg_strunescape('r','\r');
            cimg_strunescape('t','\t');
            cimg_strunescape('v','\v');
            cimg_strunescape('\\','\\');
            cimg_strunescape('\'','\'');
            cimg_strunescape('\"','\"');
            cimg_strunescape('\?','\?');
          case '0' : case '1' : case '2' : case '3' : case '4' : case '5' : case '6' : case '7' :
            val = (unsigned char)(*(ns++) - '0');
            if (*ns>='0' && *ns<='7') (val<<=3)|=*(ns++) - '0';
            if (*ns>='0' && *ns<='7') (val<<=3)|=*(ns++) - '0';
            *nd = (char)val;
            break;
          case 'x' : {
            char c = lowercase(*(++ns));
            if ((c>='0' && c<='9') || (c>='a' && c<='f')) {
              val = (unsigned char)(c<='9'?c - '0':c - 'a' + 10);
              c = lowercase(*(++ns));
              if ((c>='0' && c<='9') || (c>='a' && c<='f')) {
                (val<<=4)|=(c<='9'?c - '0':c - 'a' + 10);
                ++ns;
              }
              *nd = (char)val;
            } else *nd = c;
          } break;
          case 'u' : { // UTF-8 BMP
            char c1, c2, c3, c4;
            if ((((c1 = lowercase(ns[1]))>='0' && c1<='9') || (c1>='a' && c1<='f')) &&
                (((c2 = lowercase(ns[2]))>='0' && c2<='9') || (c2>='a' && c2<='f')) &&
                (((c3 = lowercase(ns[3]))>='0' && c3<='9') || (c3>='a' && c3<='f')) &&
                (((c4 = lowercase(ns[4]))>='0' && c4<='9') || (c4>='a' && c4<='f'))) {
              c1 = (c1<='9'?c1 - '0':c1 - 'a' + 10);
              c2 = (c2<='9'?c2 - '0':c2 - 'a' + 10);
              c3 = (c3<='9'?c3 - '0':c3 - 'a' + 10);
              c4 = (c4<='9'?c4 - '0':c4 - 'a' + 10);
              const unsigned int ival =
                ((unsigned int)c1<<12) | ((unsigned int)c2<<8) | ((unsigned int)c3<<4) | c4;
              if (ival<=0x007f) *nd = (char)ival;
              else if (ival<=0x07ff) {
                *(nd++) = (char)((ival>>6)|0xc0);
                *nd = (char)((ival&0x3f)|0x80);
              } else {
                *(nd++) = (char)((ival>>12)|0xe0);
                *(nd++) = (char)(((ival>>6)&0x3f)|0x80);
                *nd = (char)((ival&0x3f)|0x80);
              }
              ns+=5;
            } else *nd = *(ns++);
          } break;
          case 'U' : { // UTF-8 astral planes
            char c1, c2, c3, c4, c5, c6, c7, c8;
            if ((((c1 = lowercase(ns[1]))>='0' && c1<='9') || (c1>='a' && c1<='f')) &&
                (((c2 = lowercase(ns[2]))>='0' && c2<='9') || (c2>='a' && c2<='f')) &&
                (((c3 = lowercase(ns[3]))>='0' && c3<='9') || (c3>='a' && c3<='f')) &&
                (((c4 = lowercase(ns[4]))>='0' && c4<='9') || (c4>='a' && c4<='f')) &&
                (((c5 = lowercase(ns[5]))>='0' && c5<='9') || (c5>='a' && c5<='f')) &&
                (((c6 = lowercase(ns[6]))>='0' && c6<='9') || (c6>='a' && c6<='f')) &&
                (((c7 = lowercase(ns[7]))>='0' && c7<='9') || (c7>='a' && c7<='f')) &&
                (((c8 = lowercase(ns[8]))>='0' && c8<='9') || (c8>='a' && c8<='f'))) {
              c1 = (c1<='9'?c1 - '0':c1 - 'a' + 10);
              c2 = (c2<='9'?c2 - '0':c2 - 'a' + 10);
              c3 = (c3<='9'?c3 - '0':c3 - 'a' + 10);
              c4 = (c4<='9'?c4 - '0':c4 - 'a' + 10);
              c5 = (c5<='9'?c5 - '0':c5 - 'a' + 10);
              c6 = (c6<='9'?c6 - '0':c6 - 'a' + 10);
              c7 = (c7<='9'?c7 - '0':c7 - 'a' + 10);
              c8 = (c8<='9'?c8 - '0':c8 - 'a' + 10);
              const unsigned int ival =
                ((unsigned int)c1<<28) | ((unsigned int)c2<<24) | ((unsigned int)c3<<20) | ((unsigned int)c4<<16) |
                ((unsigned int)c5<<12) | ((unsigned int)c6<<8) | ((unsigned int)c7<<4) | (unsigned int)c8;
              if (ival<=0x007f) *nd = (char)ival;
              else if (ival<=0x07ff) {
                *(nd++) = (char)((ival>>6)|0xc0);
                *nd = (char)((ival&0x3f)|0x80);
              } else if (ival<=0xffff) {
                *(nd++) = (char)((ival>>12)|0xe0);
                *(nd++) = (char)(((ival>>6)&0x3f)|0x80);
                *nd = (char)((ival&0x3f)|0x80);
              } else {
                *(nd++) = (char)((ival>>18)|0xf0);
                *(nd++) = (char)(((ival>>12)&0x3f)|0x80);
                *(nd++) = (char)(((ival>>6)&0x3f)|0x80);
                *nd = (char)((ival&0x3f)|0x80);
              }
              ns+=9;
            } else *nd = *(ns++);
          } break;
          default : if (*ns) *nd = *(ns++);
          }
        else *nd = *(ns++);
    }
    inline void strunescape(char *const str) {
#define cimg_strunescape(ci,co) case ci : *nd = co; ++ns; break;

      unsigned char val = 0;
      for (char *ns = str, *nd = str; *ns || (bool)(*nd = 0); ++nd) if (*ns=='\\') switch (*(++ns)) {
            cimg_strunescape('a','\a');
            cimg_strunescape('b','\b');
            cimg_strunescape('e',0x1B);
            cimg_strunescape('f','\f');
            cimg_strunescape('n','\n');
            cimg_strunescape('r','\r');
            cimg_strunescape('t','\t');
            cimg_strunescape('v','\v');
            cimg_strunescape('\\','\\');
            cimg_strunescape('\'','\'');
            cimg_strunescape('\"','\"');
            cimg_strunescape('\?','\?');
          case '0' : case '1' : case '2' : case '3' : case '4' : case '5' : case '6' : case '7' :
            val = (unsigned char)(*(ns++) - '0');
            if (*ns>='0' && *ns<='7') (val<<=3)|=*(ns++) - '0';
            if (*ns>='0' && *ns<='7') (val<<=3)|=*(ns++) - '0';
            *nd = (char)val;
            break;
          case 'x' : {
            char c = lowercase(*(++ns));
            if ((c>='0' && c<='9') || (c>='a' && c<='f')) {
              val = (unsigned char)(c<='9'?c - '0':c - 'a' + 10);
              c = lowercase(*(++ns));
              if ((c>='0' && c<='9') || (c>='a' && c<='f')) {
                (val<<=4)|=(c<='9'?c - '0':c - 'a' + 10);
                ++ns;
              }
              *nd = (char)val;
            } else *nd = c;
          } break;
          case 'u' : {  
            char c1, c2, c3, c4;
            if ((((c1 = lowercase(ns[1]))>='0' && c1<='9') || (c1>='a' && c1<='f')) &&
                (((c2 = lowercase(ns[2]))>='0' && c2<='9') || (c2>='a' && c2<='f')) &&
                (((c3 = lowercase(ns[3]))>='0' && c3<='9') || (c3>='a' && c3<='f')) &&
                (((c4 = lowercase(ns[4]))>='0' && c4<='9') || (c4>='a' && c4<='f'))) {
              c1 = (c1<='9'?c1 - '0':c1 - 'a' + 10);
              c2 = (c2<='9'?c2 - '0':c2 - 'a' + 10);
              c3 = (c3<='9'?c3 - '0':c3 - 'a' + 10);
              c4 = (c4<='9'?c4 - '0':c4 - 'a' + 10);
              const unsigned int ival =
                ((unsigned int)c1<<12) | ((unsigned int)c2<<8) | ((unsigned int)c3<<4) | c4;
              if (ival<=0x007f) *nd = (char)ival;
              else if (ival<=0x07ff) {
                *(nd++) = (char)((ival>>6)|0xc0);
                *nd = (char)((ival&0x3f)|0x80);
              } else {
                *(nd++) = (char)((ival>>12)|0xe0);
                *(nd++) = (char)(((ival>>6)&0x3f)|0x80);
                *nd = (char)((ival&0x3f)|0x80);
              }
              ns+=5;
            } else *nd = *(ns++);
          } break;
          case 'U' : {  
            char c1, c2, c3, c4, c5, c6, c7, c8;
            if ((((c1 = lowercase(ns[1]))>='0' && c1<='9') || (c1>='a' && c1<='f')) &&
                (((c2 = lowercase(ns[2]))>='0' && c2<='9') || (c2>='a' && c2<='f')) &&
                (((c3 = lowercase(ns[3]))>='0' && c3<='9') || (c3>='a' && c3<='f')) &&
                (((c4 = lowercase(ns[4]))>='0' && c4<='9') || (c4>='a' && c4<='f')) &&
                (((c5 = lowercase(ns[5]))>='0' && c5<='9') || (c5>='a' && c5<='f')) &&
                (((c6 = lowercase(ns[6]))>='0' && c6<='9') || (c6>='a' && c6<='f')) &&
                (((c7 = lowercase(ns[7]))>='0' && c7<='9') || (c7>='a' && c7<='f')) &&
                (((c8 = lowercase(ns[8]))>='0' && c8<='9') || (c8>='a' && c8<='f'))) {
              c1 = (c1<='9'?c1 - '0':c1 - 'a' + 10);
              c2 = (c2<='9'?c2 - '0':c2 - 'a' + 10);
              c3 = (c3<='9'?c3 - '0':c3 - 'a' + 10);
              c4 = (c4<='9'?c4 - '0':c4 - 'a' + 10);
              c5 = (c5<='9'?c5 - '0':c5 - 'a' + 10);
              c6 = (c6<='9'?c6 - '0':c6 - 'a' + 10);
              c7 = (c7<='9'?c7 - '0':c7 - 'a' + 10);
              c8 = (c8<='9'?c8 - '0':c8 - 'a' + 10);
              const unsigned int ival =
                ((unsigned int)c1<<28) | ((unsigned int)c2<<24) | ((unsigned int)c3<<20) | ((unsigned int)c4<<16) |
                ((unsigned int)c5<<12) | ((unsigned int)c6<<8) | ((unsigned int)c7<<4) | (unsigned int)c8;
              if (ival<=0x007f) *nd = (char)ival;
              else if (ival<=0x07ff) {
                *(nd++) = (char)((ival>>6)|0xc0);
                *nd = (char)((ival&0x3f)|0x80);
              } else if (ival<=0xffff) {
                *(nd++) = (char)((ival>>12)|0xe0);
                *(nd++) = (char)(((ival>>6)&0x3f)|0x80);
                *nd = (char)((ival&0x3f)|0x80);
              } else {
                *(nd++) = (char)((ival>>18)|0xf0);
                *(nd++) = (char)(((ival>>12)&0x3f)|0x80);
                *(nd++) = (char)(((ival>>6)&0x3f)|0x80);
                *nd = (char)((ival&0x3f)|0x80);
              }
              ns+=9;
            } else *nd = *(ns++);
          } break;
          default : if (*ns) *nd = *(ns++);
          }
        else *nd = *(ns++);
    }