ocran 1.3.18 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.txt +306 -292
- data/LICENSE.txt +22 -22
- data/README.md +549 -533
- data/exe/ocran +5 -5
- data/ext/extconf.rb +15 -0
- data/lib/ocran/build_constants.rb +16 -16
- data/lib/ocran/build_facade.rb +17 -17
- data/lib/ocran/build_helper.rb +110 -105
- data/lib/ocran/command_output.rb +22 -22
- data/lib/ocran/dir_builder.rb +162 -0
- data/lib/ocran/direction.rb +623 -458
- data/lib/ocran/file_path_set.rb +69 -69
- data/lib/ocran/gem_spec_queryable.rb +172 -172
- data/lib/ocran/host_config_helper.rb +57 -44
- data/lib/ocran/inno_setup_script_builder.rb +111 -111
- data/lib/ocran/launcher_batch_builder.rb +85 -85
- data/lib/ocran/library_detector.rb +61 -61
- data/lib/ocran/library_detector_posix.rb +55 -0
- data/lib/ocran/option.rb +323 -273
- data/lib/ocran/refine_pathname.rb +104 -104
- data/lib/ocran/runner.rb +115 -105
- data/lib/ocran/runtime_environment.rb +46 -46
- data/lib/ocran/stub_builder.rb +298 -264
- data/lib/ocran/version.rb +5 -5
- data/lib/ocran/windows_command_escaping.rb +15 -15
- data/lib/ocran.rb +7 -7
- data/share/ocran/lzma.exe +0 -0
- data/src/Makefile +75 -0
- data/src/edicon.c +161 -0
- data/src/error.c +100 -0
- data/src/error.h +66 -0
- data/src/inst_dir.c +334 -0
- data/src/inst_dir.h +157 -0
- data/src/lzma/7zTypes.h +529 -0
- data/src/lzma/Compiler.h +43 -0
- data/src/lzma/LzmaDec.c +1363 -0
- data/src/lzma/LzmaDec.h +236 -0
- data/src/lzma/Precomp.h +10 -0
- data/src/script_info.c +246 -0
- data/src/script_info.h +7 -0
- data/src/stub.c +133 -0
- data/src/stub.manifest +29 -0
- data/src/stub.rc +3 -0
- data/src/system_utils.c +1002 -0
- data/src/system_utils.h +209 -0
- data/src/system_utils_posix.c +500 -0
- data/src/unpack.c +574 -0
- data/src/unpack.h +85 -0
- data/src/vit-ruby.ico +0 -0
- metadata +52 -16
- data/share/ocran/edicon.exe +0 -0
- data/share/ocran/stub.exe +0 -0
- data/share/ocran/stubw.exe +0 -0
data/src/Makefile
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Detect host OS
|
|
2
|
+
UNAME := $(shell uname -s 2>/dev/null || echo Windows)
|
|
3
|
+
IS_POSIX := $(filter $(UNAME),Linux Darwin)
|
|
4
|
+
|
|
5
|
+
CC := gcc
|
|
6
|
+
CFLAGS := -Wall -O2 -DWITH_LZMA -Ilzma
|
|
7
|
+
LDFLAGS := -s
|
|
8
|
+
|
|
9
|
+
ifneq ($(IS_POSIX),)
|
|
10
|
+
EXEEXT :=
|
|
11
|
+
LDLIBS :=
|
|
12
|
+
GUI_LDFLAGS :=
|
|
13
|
+
STUB_CFLAGS := $(CFLAGS) -D_CONSOLE
|
|
14
|
+
SYSTEM_UTILS_SRC := system_utils_posix.c
|
|
15
|
+
PROG_NAMES := stub
|
|
16
|
+
RESOURCE_OBJ :=
|
|
17
|
+
else
|
|
18
|
+
EXEEXT := .exe
|
|
19
|
+
LDLIBS := -lbcrypt
|
|
20
|
+
GUI_LDFLAGS := -mwindows
|
|
21
|
+
STUB_CFLAGS := $(CFLAGS) -D_CONSOLE
|
|
22
|
+
STUBW_CFLAGS := $(CFLAGS)
|
|
23
|
+
SYSTEM_UTILS_SRC := system_utils.c
|
|
24
|
+
PROG_NAMES := stub stubw edicon
|
|
25
|
+
RESOURCE_OBJ := stub.res
|
|
26
|
+
endif
|
|
27
|
+
|
|
28
|
+
BINDIR := $(CURDIR)/../share/ocran
|
|
29
|
+
BINARIES := $(addsuffix $(EXEEXT), $(PROG_NAMES))
|
|
30
|
+
|
|
31
|
+
LZMA_SRCS := lzma/LzmaDec.c
|
|
32
|
+
LZMA_OBJS := $(LZMA_SRCS:.c=.o)
|
|
33
|
+
|
|
34
|
+
COMMON_SRCS := $(SYSTEM_UTILS_SRC) inst_dir.c script_info.c unpack.c
|
|
35
|
+
COMMON_OBJS := $(COMMON_SRCS:.c=.o) $(LZMA_OBJS) $(RESOURCE_OBJ)
|
|
36
|
+
|
|
37
|
+
VARIANT_SRCS := stub.c error.c
|
|
38
|
+
CONSOLE_OBJS := $(VARIANT_SRCS:.c=_console.o)
|
|
39
|
+
WINDOW_OBJS := $(VARIANT_SRCS:.c=_window.o)
|
|
40
|
+
|
|
41
|
+
.PHONY: all clean install
|
|
42
|
+
all: $(BINARIES)
|
|
43
|
+
|
|
44
|
+
%.o: %.c
|
|
45
|
+
$(CC) $(CFLAGS) -c $< -o $@
|
|
46
|
+
|
|
47
|
+
%_console.o: %.c
|
|
48
|
+
$(CC) $(STUB_CFLAGS) -c $< -o $@
|
|
49
|
+
|
|
50
|
+
%_window.o: %.c
|
|
51
|
+
$(CC) $(STUBW_CFLAGS) -c $< -o $@
|
|
52
|
+
|
|
53
|
+
ifeq ($(IS_POSIX),)
|
|
54
|
+
stub.res: stub.rc
|
|
55
|
+
windres -i stub.rc -O coff -o stub.res
|
|
56
|
+
endif
|
|
57
|
+
|
|
58
|
+
stub$(EXEEXT): $(COMMON_OBJS) $(CONSOLE_OBJS)
|
|
59
|
+
$(CC) $(LDFLAGS) $^ $(LDLIBS) -o $@
|
|
60
|
+
|
|
61
|
+
ifeq ($(IS_POSIX),)
|
|
62
|
+
stubw$(EXEEXT): $(COMMON_OBJS) $(WINDOW_OBJS)
|
|
63
|
+
$(CC) $(LDFLAGS) $(GUI_LDFLAGS) $^ $(LDLIBS) -o $@
|
|
64
|
+
|
|
65
|
+
edicon$(EXEEXT): edicon.o
|
|
66
|
+
$(CC) $(LDFLAGS) $^ -o $@
|
|
67
|
+
endif
|
|
68
|
+
|
|
69
|
+
clean:
|
|
70
|
+
rm -f $(BINARIES) $(COMMON_OBJS) $(CONSOLE_OBJS) $(WINDOW_OBJS) \
|
|
71
|
+
edicon.o $(RESOURCE_OBJ)
|
|
72
|
+
|
|
73
|
+
install: $(BINARIES)
|
|
74
|
+
mkdir -p $(BINDIR)
|
|
75
|
+
cp -f $(BINARIES) $(BINDIR)/
|
data/src/edicon.c
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
Changes the Icon in a PE executable.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
#include <windows.h>
|
|
6
|
+
#include <stdio.h>
|
|
7
|
+
|
|
8
|
+
#pragma pack(push, 2)
|
|
9
|
+
|
|
10
|
+
/* Icon file header */
|
|
11
|
+
typedef struct
|
|
12
|
+
{
|
|
13
|
+
WORD Reserved;
|
|
14
|
+
WORD ResourceType;
|
|
15
|
+
WORD ImageCount;
|
|
16
|
+
} IconFileHeader;
|
|
17
|
+
|
|
18
|
+
/* Icon File directory entry structure */
|
|
19
|
+
typedef struct
|
|
20
|
+
{
|
|
21
|
+
BYTE Width;
|
|
22
|
+
BYTE Height;
|
|
23
|
+
BYTE Colors;
|
|
24
|
+
BYTE Reserved;
|
|
25
|
+
WORD Planes;
|
|
26
|
+
WORD BitsPerPixel;
|
|
27
|
+
DWORD ImageSize;
|
|
28
|
+
DWORD ImageOffset;
|
|
29
|
+
} IconDirectoryEntry;
|
|
30
|
+
|
|
31
|
+
/* Group Icon Resource directory entry structure */
|
|
32
|
+
typedef struct
|
|
33
|
+
{
|
|
34
|
+
BYTE Width;
|
|
35
|
+
BYTE Height;
|
|
36
|
+
BYTE Colors;
|
|
37
|
+
BYTE Reserved;
|
|
38
|
+
WORD Planes;
|
|
39
|
+
WORD BitsPerPixel;
|
|
40
|
+
DWORD ImageSize;
|
|
41
|
+
WORD ResourceID;
|
|
42
|
+
} IconDirResEntry, *PIconDirResEntry;
|
|
43
|
+
|
|
44
|
+
/* Group Icon Structore (RT_GROUP_ICON) */
|
|
45
|
+
typedef struct
|
|
46
|
+
{
|
|
47
|
+
WORD Reserved;
|
|
48
|
+
WORD ResourceType;
|
|
49
|
+
WORD ImageCount;
|
|
50
|
+
IconDirResEntry Enries[0]; /* Number of these is in ImageCount */
|
|
51
|
+
} GroupIcon;
|
|
52
|
+
|
|
53
|
+
#pragma pack(pop)
|
|
54
|
+
|
|
55
|
+
BOOL UpdateIcon(LPTSTR ExecutableFileName, LPTSTR IconFileName)
|
|
56
|
+
{
|
|
57
|
+
HANDLE h = BeginUpdateResource(ExecutableFileName, FALSE);
|
|
58
|
+
if (h == INVALID_HANDLE_VALUE)
|
|
59
|
+
{
|
|
60
|
+
printf("Failed to BeginUpdateResource\n");
|
|
61
|
+
return FALSE;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/* Read the Icon file */
|
|
65
|
+
HANDLE hIconFile = CreateFile(IconFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
|
|
66
|
+
if (hIconFile == INVALID_HANDLE_VALUE)
|
|
67
|
+
{
|
|
68
|
+
fprintf(stderr, "Failed to open icon file.\n");
|
|
69
|
+
return FALSE;
|
|
70
|
+
}
|
|
71
|
+
DWORD Size = GetFileSize(hIconFile, NULL);
|
|
72
|
+
BYTE* Data = LocalAlloc(LMEM_FIXED, Size);
|
|
73
|
+
DWORD BytesRead;
|
|
74
|
+
if (!ReadFile(hIconFile, Data, Size, &BytesRead, NULL))
|
|
75
|
+
{
|
|
76
|
+
fprintf(stderr, "Failed to read icon file.\n");
|
|
77
|
+
return FALSE;
|
|
78
|
+
}
|
|
79
|
+
CloseHandle(hIconFile);
|
|
80
|
+
|
|
81
|
+
IconFileHeader* header = (IconFileHeader*)Data;
|
|
82
|
+
IconDirectoryEntry* entries = (IconDirectoryEntry*)(header + 1);
|
|
83
|
+
|
|
84
|
+
/* Validate that all directory entries fit within the file */
|
|
85
|
+
DWORD entriesEnd = sizeof(IconFileHeader) + header->ImageCount * sizeof(IconDirectoryEntry);
|
|
86
|
+
if (entriesEnd > Size)
|
|
87
|
+
{
|
|
88
|
+
fprintf(stderr, "Icon file too small for declared ImageCount.\n");
|
|
89
|
+
LocalFree(Data);
|
|
90
|
+
return FALSE;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/* Create the RT_ICON resources */
|
|
94
|
+
int i;
|
|
95
|
+
for (i = 0; i < header->ImageCount; ++i)
|
|
96
|
+
{
|
|
97
|
+
if (entries[i].ImageOffset + entries[i].ImageSize > Size)
|
|
98
|
+
{
|
|
99
|
+
fprintf(stderr, "Icon entry %d exceeds file bounds.\n", i);
|
|
100
|
+
LocalFree(Data);
|
|
101
|
+
return FALSE;
|
|
102
|
+
}
|
|
103
|
+
BOOL b = UpdateResource(h, MAKEINTRESOURCE(RT_ICON), MAKEINTRESOURCE(101 + i), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), Data + entries[i].ImageOffset, entries[i].ImageSize);
|
|
104
|
+
if (!b)
|
|
105
|
+
{
|
|
106
|
+
fprintf(stderr, "failed to UpdateResource %lu\n", GetLastError());
|
|
107
|
+
return FALSE;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/* Create the RT_GROUP_ICON structure */
|
|
112
|
+
DWORD GroupIconSize = sizeof(GroupIcon) + header->ImageCount * sizeof(IconDirectoryEntry);
|
|
113
|
+
GroupIcon* gi = (GroupIcon*)LocalAlloc(LMEM_FIXED, GroupIconSize);
|
|
114
|
+
gi->Reserved = 0;
|
|
115
|
+
gi->ResourceType = header->ResourceType;
|
|
116
|
+
gi->ImageCount = header->ImageCount;
|
|
117
|
+
for (i = 0; i < header->ImageCount; ++i)
|
|
118
|
+
{
|
|
119
|
+
IconDirResEntry* e = &gi->Enries[i];
|
|
120
|
+
e->Width = entries[i].Width;
|
|
121
|
+
e->Height = entries[i].Height;
|
|
122
|
+
e->Colors = entries[i].Colors;
|
|
123
|
+
e->Reserved = entries[i].Reserved;
|
|
124
|
+
e->Planes = entries[i].Planes;
|
|
125
|
+
e->BitsPerPixel = entries[i].BitsPerPixel;
|
|
126
|
+
e->ImageSize = entries[i].ImageSize;
|
|
127
|
+
e->ResourceID = 101 + i;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/* Save the RT_GROUP_ICON resource */
|
|
131
|
+
BOOL b = UpdateResource(h, MAKEINTRESOURCE(RT_GROUP_ICON), MAKEINTRESOURCE(100), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), gi, GroupIconSize);
|
|
132
|
+
if (!b)
|
|
133
|
+
{
|
|
134
|
+
fprintf(stderr, "Failed to create group icon.\n");
|
|
135
|
+
return FALSE;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (!EndUpdateResource(h, FALSE))
|
|
139
|
+
{
|
|
140
|
+
fprintf(stderr, "Failed to EndUpdateResource.\n");
|
|
141
|
+
return FALSE;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return TRUE;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
int main(int argc, char* argv[])
|
|
148
|
+
{
|
|
149
|
+
if (argc == 3)
|
|
150
|
+
{
|
|
151
|
+
if (UpdateIcon(argv[1], argv[2]))
|
|
152
|
+
return 0;
|
|
153
|
+
else
|
|
154
|
+
return -1;
|
|
155
|
+
}
|
|
156
|
+
else
|
|
157
|
+
{
|
|
158
|
+
fprintf(stderr, "Usage: edicon.exe <exefile> <icofile>\n");
|
|
159
|
+
return -1;
|
|
160
|
+
}
|
|
161
|
+
}
|
data/src/error.c
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
#ifdef _WIN32
|
|
2
|
+
#include <windows.h>
|
|
3
|
+
#endif
|
|
4
|
+
#include <string.h>
|
|
5
|
+
#include <stdio.h>
|
|
6
|
+
#include <stdarg.h>
|
|
7
|
+
#include <stdbool.h>
|
|
8
|
+
#include "error.h"
|
|
9
|
+
|
|
10
|
+
static bool debug_mode = false;
|
|
11
|
+
|
|
12
|
+
// Enable debug mode
|
|
13
|
+
void EnableDebugMode()
|
|
14
|
+
{
|
|
15
|
+
debug_mode = true;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
static bool vformat_message(char *buffer, size_t buffer_size, const char *label,
|
|
19
|
+
const char* format, va_list args)
|
|
20
|
+
{
|
|
21
|
+
if (!buffer || buffer_size < 2) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
size_t offset = 0;
|
|
26
|
+
|
|
27
|
+
if (label) {
|
|
28
|
+
int label_needed = snprintf(buffer, buffer_size, "%s: ", label);
|
|
29
|
+
if (label_needed < 0 || (size_t)label_needed >= buffer_size) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
offset = (size_t)label_needed;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
int needed = vsnprintf(buffer + offset, buffer_size - offset, format, args);
|
|
36
|
+
if (needed < 0) {
|
|
37
|
+
return false;
|
|
38
|
+
} else if ((size_t)needed >= buffer_size - offset - 2) {
|
|
39
|
+
offset = buffer_size - 2;
|
|
40
|
+
} else {
|
|
41
|
+
offset += (size_t)needed;
|
|
42
|
+
}
|
|
43
|
+
buffer[offset++] = '\n';
|
|
44
|
+
buffer[offset] = '\0';
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
static void print_message(const char *label, const char *format, va_list args)
|
|
49
|
+
{
|
|
50
|
+
char text[4096];
|
|
51
|
+
if (vformat_message(text, sizeof(text), label, format, args)) {
|
|
52
|
+
fputs(text, stderr);
|
|
53
|
+
} else {
|
|
54
|
+
fputs("log message formatting failed\n", stderr);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Prints a fatal error message to stderr.
|
|
59
|
+
void PrintFatalMessage(const char *format, ...)
|
|
60
|
+
{
|
|
61
|
+
va_list args;
|
|
62
|
+
va_start(args, format);
|
|
63
|
+
print_message("FATAL", format, args);
|
|
64
|
+
va_end(args);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Displays a fatal error message via a message box.
|
|
68
|
+
#ifdef _WIN32
|
|
69
|
+
void PrintFatalMessageBox(const char *format, ...)
|
|
70
|
+
{
|
|
71
|
+
char TextBuffer[1024];
|
|
72
|
+
va_list args;
|
|
73
|
+
va_start(args, format);
|
|
74
|
+
vsnprintf(TextBuffer, 1024, format, args);
|
|
75
|
+
va_end(args);
|
|
76
|
+
MessageBox(NULL, TextBuffer, "OCRAN", MB_OK | MB_ICONWARNING);
|
|
77
|
+
}
|
|
78
|
+
#endif
|
|
79
|
+
|
|
80
|
+
// Prints an application level error message to stderr if in debug mode.
|
|
81
|
+
void PrintAppErrorMessage(const char *format, ...)
|
|
82
|
+
{
|
|
83
|
+
if (!debug_mode) return;
|
|
84
|
+
|
|
85
|
+
va_list args;
|
|
86
|
+
va_start(args, format);
|
|
87
|
+
print_message("ERROR", format, args);
|
|
88
|
+
va_end(args);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Prints a debug message to stderr if in debug mode.
|
|
92
|
+
void PrintDebugMessage(const char *format, ...)
|
|
93
|
+
{
|
|
94
|
+
if (!debug_mode) return;
|
|
95
|
+
|
|
96
|
+
va_list args;
|
|
97
|
+
va_start(args, format);
|
|
98
|
+
print_message("DEBUG", format, args);
|
|
99
|
+
va_end(args);
|
|
100
|
+
}
|
data/src/error.h
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#include <stdarg.h>
|
|
2
|
+
|
|
3
|
+
// Exit code definitions
|
|
4
|
+
#define EXIT_CODE_SUCCESS (0)
|
|
5
|
+
#define EXIT_CODE_FAILURE (-1)
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* EnableDebugMode - Enable debug mode
|
|
9
|
+
*
|
|
10
|
+
* This function enables debug mode and attaches a console for debug output.
|
|
11
|
+
*/
|
|
12
|
+
void EnableDebugMode();
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* PrintFatalMessage - Prints a fatal error message to stderr.
|
|
16
|
+
*
|
|
17
|
+
* This function prints a formatted error message to stderr. The message is
|
|
18
|
+
* prefixed with "FATAL: ".
|
|
19
|
+
*
|
|
20
|
+
* @param format The format of the error message to be displayed. This is a printf-like
|
|
21
|
+
* format string followed by additional arguments.
|
|
22
|
+
*/
|
|
23
|
+
void PrintFatalMessage(const char *format, ...);
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* PrintFatalMessageBox - Displays a fatal error message via a message box.
|
|
27
|
+
*
|
|
28
|
+
* This function displays a formatted error message in a message box with the
|
|
29
|
+
* caption 'OCRAN' and an icon of MB_ICONWARNING.
|
|
30
|
+
*
|
|
31
|
+
* @param format The format of the error message to be displayed. This is a printf-like
|
|
32
|
+
* format string followed by additional arguments.
|
|
33
|
+
*/
|
|
34
|
+
#ifdef _WIN32
|
|
35
|
+
void PrintFatalMessageBox(const char *format, ...);
|
|
36
|
+
#endif
|
|
37
|
+
|
|
38
|
+
#if defined(_CONSOLE) || !defined(_WIN32)
|
|
39
|
+
#define FATAL(...) PrintFatalMessage(__VA_ARGS__)
|
|
40
|
+
#else
|
|
41
|
+
#define FATAL(...) PrintFatalMessageBox(__VA_ARGS__)
|
|
42
|
+
#endif
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* PrintAppErrorMessage - Prints an application level error message to stderr if in debug mode.
|
|
46
|
+
*
|
|
47
|
+
* This function prints a formatted error message to stderr. The message is
|
|
48
|
+
* prefixed with "ERROR: ".
|
|
49
|
+
*
|
|
50
|
+
* @param format The format of the error message to be displayed. This is a printf-like
|
|
51
|
+
* format string followed by additional arguments.
|
|
52
|
+
*/
|
|
53
|
+
void PrintAppErrorMessage(const char *format, ...);
|
|
54
|
+
#define APP_ERROR(...) PrintAppErrorMessage(__VA_ARGS__)
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* PrintDebugMessage - Prints a debug message to stderr if in debug mode.
|
|
58
|
+
*
|
|
59
|
+
* This function prints a formatted debug message to stderr. The message is
|
|
60
|
+
* prefixed with "DEBUG: ".
|
|
61
|
+
*
|
|
62
|
+
* @param format The format of the debug message to be displayed. This is a printf-like
|
|
63
|
+
* format string followed by additional arguments.
|
|
64
|
+
*/
|
|
65
|
+
void PrintDebugMessage(const char *format, ...);
|
|
66
|
+
#define DEBUG(...) PrintDebugMessage(__VA_ARGS__)
|