summaryrefslogtreecommitdiffstats
path: root/apps/plugins/puzzles/compress.c
blob: 78b2aa4d4a1c65caa51117afa9442ce3d4603ceb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
/* This program compresses the help content found in help/ to standard
 * output. Do not directly compile or use this program, instead it
 * will be automatically used by genhelp.sh */

#include <assert.h>
#include <ctype.h>
#include <lz4hc.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "help.h"

char *compress(const char *in, int inlen, int *rc, int *minsz, int *minlev)
{
    int maxsz = LZ4_compressBound(inlen);
    unsigned char *outbuf = malloc(maxsz);
    *minsz = 9999999;
    *minlev = 0;

    for(int i = LZ4HC_CLEVEL_MIN; i < LZ4HC_CLEVEL_MAX; ++i)
    {
        *rc = LZ4_compress_HC(in, outbuf, inlen, maxsz, i);
        if(!*rc)
        {
            fprintf(stderr, "compress failed\n");
            return NULL;
        }
        if(*rc < *minsz)
        {
            *minsz = *rc;
            *minlev = i;
        }
    }
    *rc = LZ4_compress_HC(in, outbuf, inlen, maxsz, *minlev);
    return outbuf;
}

void dump_bytes(unsigned char *buf, int len)
{
    int i = 0;
    while(i < len)
    {
        int stop = i + 10;
        for(;i < stop && i < len; ++i)
        {
            unsigned char c = buf[i];
            printf("0x%02x,", c);
            if(i != stop - 1 && i != len - 1)
                printf(" ");
        }
        printf("\n");
    }
}

/* Input: help text in help_text array (to be compiled against) as a
 * standard C string
 * Output: C source code on stdout defining the following:
 *
 * const char help_text_words[];
 * const unsigned short help_text_len;
 * struct style_text help_text_style[];
 *
 * help_text_words consists of help_text_len bytes containing the
 * words of the help text, delimited with NULs, not a standard C
 * string. The rockbox frontend is responsible for generating an array
 * of pointers into this array to pass to display_text. */

int main()
{
    int inlen = strlen(help_text) + 1, outlen;
    int minsz, minlev;

    printf("/* auto-generated on " __DATE__ " by genhelp.sh */\n");
    printf("/* help text is compressed using LZ4; see compress.c for details */\n");
    printf("/* DO NOT EDIT! */\n\n");

    printf("#include \"lib/display_text.h\"\n\n");

    printf("struct style_text help_text_style[] = {\n");

    /* break up words on spaces and newline while printing indices of
     * underlined words */
    char *buf = strdup(help_text);
    bool underline = false, center = false;
    int word_idx = 0, help_text_len = inlen;

    for(int i = 0; i < help_text_len; ++i)
    {
        switch(buf[i])
        {
        case '#':
        case '_':
            /* title or underlined portion */
            if(buf[i] == '#')
            {
                center = !center;
                if(center)
                    printf(" { %d, TEXT_CENTER | C_RED },\n", word_idx);
            }
            else
            {
                /* underline */
                underline = !underline;

                if(underline)
                    printf(" { %d, TEXT_UNDERLINE },\n", word_idx);
            }

            /* delete the formatting character */
            memmove(buf + i, buf + i + 1, help_text_len - i - 1);
            --help_text_len;
            --i;
            break;
        case '$':
            /* genhelp.sh replaces the underscores in URLs with
             * dollar signs to help us out. */
            buf[i] = '_';
            break;
        case '\n':
            center = false;
            /* fall through */
        case ' ':
        case '\0':
            /* Groups of words that are centered or underlined are
             * counted as a single "word". */
            if(!underline && !center)
            {
                buf[i] = '\0';
                ++word_idx;
            }
            break;
        }
    }
    /* sanity check */
    int words_check = 0;
    for(int i = 0; i < help_text_len; ++i)
        if(!buf[i])
            ++words_check;

    assert(words_check == word_idx);

    printf(" LAST_STYLE_ITEM\n};\n\n");

    /* remove trailing NULs */
    for(int i = help_text_len - 2; i >= 0 && !buf[i]; --i, --help_text_len, --word_idx);

    unsigned char *outbuf = compress(buf, help_text_len, &outlen, &minsz, &minlev);

    printf("/* orig %d comp %d ratio %g level %d saved %d */\n", help_text_len, minsz, (double)minsz / help_text_len, minlev, help_text_len - minsz);
    printf("const char help_text[] = {\n");

    dump_bytes(outbuf, outlen);
    free(outbuf);
    free(buf);

    printf("};\n\n");
    printf("const unsigned short help_text_len = %d;\n", help_text_len);
    printf("const unsigned short help_text_words = %d;\n", word_idx);

    return 0;
}