diff --git a/Makefile b/Makefile
index da790f57..2fc80ff5 100644
--- a/Makefile
+++ b/Makefile
@@ -39,7 +39,7 @@ SSOBJS = $(OBJDIR)\solvespace.obj \
RES = $(OBJDIR)\resource.res
-LIBS = user32.lib gdi32.lib comctl32.lib advapi32.lib opengl32.lib glu32.lib \
+LIBS = user32.lib gdi32.lib comctl32.lib advapi32.lib shell32.lib opengl32.lib glu32.lib \
extlib\libpng.lib extlib\zlib.lib
all: $(OBJDIR)/solvespace.exe
diff --git a/doc/reference.txt b/doc/reference.txt
index 8acf2b11..18d36995 100644
--- a/doc/reference.txt
+++ b/doc/reference.txt
@@ -1304,7 +1304,7 @@ for new users; to learn about this program, see the video tutorials.
Licensing
As downloaded, MechSketch does not include a license key. This means
- that it cannot create files with more than seven groups. Larger files
+ that it cannot create files with more than six groups. Larger files
may be opened, but not modified. This light version of MechSketch
is intended for evaluation, but non-commercial / personal use is
also permitted.
diff --git a/export.cpp b/export.cpp
index 7f6dca10..d2570b3a 100644
--- a/export.cpp
+++ b/export.cpp
@@ -117,46 +117,46 @@ havepoly:
// Some software, like Adobe Illustrator, insists on a header.
fprintf(f,
-" 999\n"
-"file created by SolveSpace\n"
-" 0\n"
-"SECTION\n"
-" 2\n"
-"HEADER\n"
-" 9\n"
-"$ACADVER\n"
-" 1\n"
-"AC1006\n"
-" 9\n"
-"$INSBASE\n"
-" 10\n"
-"0.0\n"
-" 20\n"
-"0.0\n"
-" 30\n"
-"0.0\n"
-" 9\n"
-"$EXTMIN\n"
-" 10\n"
-"0.0\n"
-" 20\n"
-"0.0\n"
-" 9\n"
-"$EXTMAX\n"
-" 10\n"
-"10000.0\n"
-" 20\n"
-"10000.0\n"
-" 0\n"
-"ENDSEC\n");
+" 999\r\n"
+"file created by SolveSpace\r\n"
+" 0\r\n"
+"SECTION\r\n"
+" 2\r\n"
+"HEADER\r\n"
+" 9\r\n"
+"$ACADVER\r\n"
+" 1\r\n"
+"AC1006\r\n"
+" 9\r\n"
+"$INSBASE\r\n"
+" 10\r\n"
+"0.0\r\n"
+" 20\r\n"
+"0.0\r\n"
+" 30\r\n"
+"0.0\r\n"
+" 9\r\n"
+"$EXTMIN\r\n"
+" 10\r\n"
+"0.0\r\n"
+" 20\r\n"
+"0.0\r\n"
+" 9\r\n"
+"$EXTMAX\r\n"
+" 10\r\n"
+"10000.0\r\n"
+" 20\r\n"
+"10000.0\r\n"
+" 0\r\n"
+"ENDSEC\r\n");
// Now begin the entities, which are just line segments reproduced from
// our piecewise linear curves.
fprintf(f,
-" 0\n"
-"SECTION\n"
-" 2\n"
-"ENTITIES\n");
+" 0\r\n"
+"SECTION\r\n"
+" 2\r\n"
+"ENTITIES\r\n");
int i, j;
for(i = 0; i < sp->l.n; i++) {
@@ -172,22 +172,22 @@ havepoly:
double s = SS.exportScale;
fprintf(f,
-" 0\n"
-"LINE\n"
-" 8\n" // Layer code
-"%d\n"
-" 10\n" // xA
-"%.6f\n"
-" 20\n" // yA
-"%.6f\n"
-" 30\n" // zA
-"%.6f\n"
-" 11\n" // xB
-"%.6f\n"
-" 21\n" // yB
-"%.6f\n"
-" 31\n" // zB
-"%.6f\n",
+" 0\r\n"
+"LINE\r\n"
+" 8\r\n" // Layer code
+"%d\r\n"
+" 10\r\n" // xA
+"%.6f\r\n"
+" 20\r\n" // yA
+"%.6f\r\n"
+" 30\r\n" // zA
+"%.6f\r\n"
+" 11\r\n" // xB
+"%.6f\r\n"
+" 21\r\n" // yB
+"%.6f\r\n"
+" 31\r\n" // zB
+"%.6f\r\n",
0,
e0.x/s, e0.y/s, 0.0,
e1.x/s, e1.y/s, 0.0);
@@ -195,10 +195,10 @@ havepoly:
}
fprintf(f,
-" 0\n"
-"ENDSEC\n"
-" 0\n"
-"EOF\n" );
+" 0\r\n"
+"ENDSEC\r\n"
+" 0\r\n"
+"EOF\r\n" );
spa.Clear();
fclose(f);
diff --git a/graphicswin.cpp b/graphicswin.cpp
index 32ac73b8..af2b7b41 100644
--- a/graphicswin.cpp
+++ b/graphicswin.cpp
@@ -7,6 +7,7 @@
#define mFile (&SolveSpace::MenuFile)
#define mGrp (&Group::MenuGroup)
#define mAna (&SolveSpace::MenuAnalyze)
+#define mHelp (&SolveSpace::MenuHelp)
#define S 0x100
#define C 0x200
const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = {
@@ -101,11 +102,11 @@ const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = {
{ 1, "&Stop Tracing...\tCtrl+Shift+S", MNU_STOP_TRACING, 'S'|S|C,mAna },
{ 1, "Step &Dimension...\tCtrl+Shift+D", MNU_STEP_DIM, 'D'|S|C,mAna },
-{ 0, "&Help", 0, NULL },
-{ 1, "&Load License...", 0, NULL },
-{ 1, NULL, 0, NULL },
-{ 1, "&Website / Manual", 0, NULL },
-{ 1, "&About", 0, NULL },
+{ 0, "&Help", 0, 0, NULL },
+{ 1, "&Load License...", MNU_LICENSE, 0, mHelp },
+{ 1, NULL, 0, 0, NULL },
+{ 1, "&Website / Manual", MNU_WEBSITE, 0, mHelp },
+{ 1, "&About", MNU_ABOUT, 0, mHelp },
{ -1 },
};
diff --git a/group.cpp b/group.cpp
index ffb36057..536ffde2 100644
--- a/group.cpp
+++ b/group.cpp
@@ -17,6 +17,14 @@ void Group::AddParam(IdList *param, hParam hp, double v) {
}
void Group::MenuGroup(int id) {
+ if(!SS.license.licensed && SS.group.n >= 7) {
+ Error("The free version of this software does not support more "
+ "than six groups.\r\n\r\n"
+ "To remove this restriction, please choose Help -> "
+ "Website / Manual, and purchase a license.");
+ return;
+ }
+
Group g;
ZERO(&g);
g.visible = true;
diff --git a/keygen.pl b/keygen.pl
new file mode 100644
index 00000000..42dec69f
--- /dev/null
+++ b/keygen.pl
@@ -0,0 +1,64 @@
+#!/usr/bin/perl
+
+$POLY = 0xedb88320;
+
+sub ProcessBit {
+ my ($bit) = @_;
+
+ my $topWasSet = ($shiftReg & (1 << 31));
+
+ $shiftReg <<= 1;
+ if($bit) {
+ $shiftReg |= 1;
+ }
+
+ if($topWasSet) {
+ $shiftReg ^= $POLY;
+ }
+}
+
+sub ProcessByte {
+ my ($v) = @_;
+ for(0..7) {
+ ProcessBit($v & (1 << $_));
+ }
+}
+
+sub ProcessString {
+ my ($str) = @_;
+ for (split //, $str) {
+ if($_ ne "\n" and $_ ne "\r") {
+ ProcessByte(ord($_));
+ }
+ }
+}
+
+sub LicenseKey {
+ my @MAGIC = ( 203, 244, 134, 225, 45, 250, 70, 65,
+ 224, 189, 35, 3, 228, 51, 77, 169, );
+ my $magic = join('', map { chr($_) } @MAGIC);
+
+ my ($line1, $line2, $users) = @_;
+
+ $shiftReg = 0;
+ ProcessString($line1);
+ ProcessString($line2);
+ ProcessString($users);
+ ProcessString($magic);
+
+ my $key = '±²³MechSketchLicense' . "\n";
+ $key .= $line1 . "\n";
+ $key .= $line2 . "\n";
+ $key .= $users . "\n";
+ $key .= sprintf("%08x\n", $shiftReg);
+
+ return $key;
+}
+
+
+$line1 = "Jonathan Westhues";
+$line2 = "";
+$users = "single-user license";
+
+print LicenseKey($line1, $line2, $users);
+
diff --git a/solvespace.cpp b/solvespace.cpp
index a27ff9e1..ff404e49 100644
--- a/solvespace.cpp
+++ b/solvespace.cpp
@@ -2,7 +2,21 @@
SolveSpace SS;
+void SolveSpace::CheckLicenseFromRegistry(void) {
+ // First, let's see if we're running licensed or free
+ CnfThawString(license.line1, sizeof(license.line1), "LicenseLine1");
+ CnfThawString(license.line2, sizeof(license.line2), "LicenseLine2");
+ CnfThawString(license.users, sizeof(license.users), "LicenseUsers");
+ license.key = CnfThawDWORD(0, "LicenseKey");
+
+ license.licensed =
+ LicenseValid(license.line1, license.line2, license.users, license.key);
+}
+
void SolveSpace::Init(char *cmdLine) {
+ CheckLicenseFromRegistry();
+
+ // Then, load the registry settings.
int i;
// Default list of colors for the model material
modelColor[0] = CnfThawDWORD(RGB(150, 150, 150), "ModelColor_0");
@@ -431,14 +445,14 @@ void SolveSpace::MenuAnalyze(int id) {
case GraphicsWindow::MNU_STOP_TRACING: {
char exportFile[MAX_PATH] = "";
if(GetSaveFile(exportFile, CSV_EXT, CSV_PATTERN)) {
- FILE *f = fopen(exportFile, "w");
+ FILE *f = fopen(exportFile, "wb");
if(f) {
int i;
SContour *sc = &(SS.traced.path);
for(i = 0; i < sc->l.n; i++) {
Vector p = sc->l.elem[i].p;
double s = SS.exportScale;
- fprintf(f, "%.10f, %.10f, %.10f\n",
+ fprintf(f, "%.10f, %.10f, %.10f\r\n",
p.x/s, p.y/s, p.z/s);
}
fclose(f);
@@ -457,3 +471,126 @@ void SolveSpace::MenuAnalyze(int id) {
}
}
+void SolveSpace::Crc::ProcessBit(int bit) {
+ bool topWasSet = ((shiftReg & (1 << 31)) != 0);
+
+ shiftReg <<= 1;
+ if(bit) {
+ shiftReg |= 1;
+ }
+
+ if(topWasSet) {
+ shiftReg ^= POLY;
+ }
+}
+
+void SolveSpace::Crc::ProcessByte(BYTE b) {
+ int i;
+ for(i = 0; i < 8; i++) {
+ ProcessBit(b & (1 << i));
+ }
+}
+
+void SolveSpace::Crc::ProcessString(char *s) {
+ for(; *s; s++) {
+ if(*s != '\n' && *s != '\r') {
+ ProcessByte((BYTE)*s);
+ }
+ }
+}
+
+bool SolveSpace::LicenseValid(char *line1, char *line2, char *users, DWORD key)
+{
+ BYTE magic[17] = {
+ 203, 244, 134, 225, 45, 250, 70, 65,
+ 224, 189, 35, 3, 228, 51, 77, 169,
+ 0
+ };
+
+ crc.shiftReg = 0;
+ crc.ProcessString(line1);
+ crc.ProcessString(line2);
+ crc.ProcessString(users);
+ crc.ProcessString((char *)magic);
+
+ return (key == crc.shiftReg);
+}
+
+void SolveSpace::CleanEol(char *in) {
+ char *s;
+ s = strchr(in, '\r');
+ if(s) *s = '\0';
+ s = strchr(in, '\n');
+ if(s) *s = '\0';
+}
+
+void SolveSpace::LoadLicenseFile(char *filename) {
+ FILE *f = fopen(filename, "rb");
+ if(!f) {
+ Error("Couldn't open file '%s'", filename);
+ return;
+ }
+
+ char buf[100];
+ fgets(buf, sizeof(buf), f);
+ char *str = "±²³MechSketchLicense";
+ if(memcmp(buf, str, strlen(str)) != 0) {
+ fclose(f);
+ Error("This is not a license file,");
+ return;
+ }
+
+ char line1[512], line2[512], users[512];
+ fgets(line1, sizeof(line1), f);
+ CleanEol(line1);
+ fgets(line2, sizeof(line2), f);
+ CleanEol(line2);
+ fgets(users, sizeof(users), f);
+ CleanEol(users);
+
+ fgets(buf, sizeof(buf), f);
+ DWORD key = 0;
+ sscanf(buf, "%x", &key);
+
+ if(LicenseValid(line1, line2, users, key)) {
+ // Install the new key
+ CnfFreezeString(line1, "LicenseLine1");
+ CnfFreezeString(line2, "LicenseLine2");
+ CnfFreezeString(users, "LicenseUsers");
+ CnfFreezeDWORD(key, "LicenseKey");
+ Message("License key successfully installed.");
+
+ // This updates our display in the text window to show that we're
+ // licensed now.
+ CheckLicenseFromRegistry();
+ SS.later.showTW = true;
+ } else {
+ Error("License key invalid.");
+ }
+ fclose(f);
+}
+
+void SolveSpace::MenuHelp(int id) {
+ switch(id) {
+ case GraphicsWindow::MNU_WEBSITE:
+ OpenWebsite("http://www.mechsketch.com/helpmenu");
+ break;
+
+ case GraphicsWindow::MNU_ABOUT:
+ Message("This is MechSketch version 0.1.\r\n\r\n"
+ "For more information, see http://www.mechsketch.com/\r\n\r\n"
+ "Built " __TIME__ " " __DATE__ ".\r\n\r\n"
+ "Copyright 2008 Jonathan Westhues, All Rights Reserved.");
+ break;
+
+ case GraphicsWindow::MNU_LICENSE: {
+ char licenseFile[MAX_PATH] = "";
+ if(GetOpenFile(licenseFile, LICENSE_EXT, LICENSE_PATTERN)) {
+ SS.LoadLicenseFile(licenseFile);
+ }
+ break;
+ }
+
+ default: oops();
+ }
+}
diff --git a/solvespace.h b/solvespace.h
index 842061c8..f0b8e941 100644
--- a/solvespace.h
+++ b/solvespace.h
@@ -64,11 +64,16 @@ int SaveFileYesNoCancel(void);
#define DXF_EXT "dxf"
#define CSV_PATTERN "CSV File (*.csv)\0*.csv\0All Files (*)\0*\0\0"
#define CSV_EXT "csv"
+#define LICENSE_PATTERN \
+ "License File (*.license)\0*.license\0All Files (*)\0*\0\0"
+#define LICENSE_EXT "license"
BOOL GetSaveFile(char *file, char *defExtension, char *selPattern);
BOOL GetOpenFile(char *file, char *defExtension, char *selPattern);
void GetAbsoluteFilename(char *file);
void LoadAllFontFiles(void);
+void OpenWebsite(char *url);
+
void CheckMenuById(int id, BOOL checked);
void EnableMenuById(int id, BOOL checked);
@@ -92,6 +97,7 @@ void dbp(char *str, ...);
CO((tri).a), CO((tri).b), CO((tri).c))
void SetWindowTitle(char *str);
+void Message(char *str, ...);
void Error(char *str, ...);
void ExitNow(void);
@@ -376,6 +382,7 @@ public:
// The platform-dependent code calls this before entering the msg loop
void Init(char *cmdLine);
+ void CheckLicenseFromRegistry(void);
void Exit(void);
// File load/save routines, including the additional files that get
@@ -464,6 +471,29 @@ public:
bool generateAll;
} later;
void DoLater(void);
+
+ // For the licensing
+ class Crc {
+public:
+ static const DWORD POLY = 0xedb88320;
+ DWORD shiftReg;
+
+ void ProcessBit(int bit);
+ void ProcessByte(BYTE b);
+ void ProcessString(char *s);
+ };
+ Crc crc;
+ struct {
+ bool licensed;
+ char line1[512];
+ char line2[512];
+ char users[512];
+ DWORD key;
+ } license;
+ static void MenuHelp(int id);
+ void CleanEol(char *s);
+ void LoadLicenseFile(char *filename);
+ bool LicenseValid(char *line1, char *line2, char *users, DWORD key);
};
extern SolveSpace SS;
diff --git a/textscreens.cpp b/textscreens.cpp
index 4913d289..724bdec6 100644
--- a/textscreens.cpp
+++ b/textscreens.cpp
@@ -98,6 +98,9 @@ void TextWindow::ScreenHowGroupSolved(int link, DWORD v) {
void TextWindow::ScreenShowConfiguration(int link, DWORD v) {
SS.TW.GoToScreen(SCREEN_CONFIGURATION);
}
+void TextWindow::ScreenGoToWebsite(int link, DWORD v) {
+ OpenWebsite("http://www.mechsketch.com/txtlink");
+}
void TextWindow::ShowListOfGroups(void) {
Printf(true, "%Ftactv show ok group-name%E");
int i;
@@ -140,6 +143,22 @@ void TextWindow::ShowListOfGroups(void) {
&(TextWindow::ScreenShowGroupsSpecial));
Printf(false, " %Fl%Ls%fconfiguration%E",
&(TextWindow::ScreenShowConfiguration));
+
+ // Show license info
+ Printf(false, "");
+ if(SS.license.licensed) {
+ Printf(false, "%FtLicensed to:");
+ Printf(false, "%Fg %s", SS.license.line1);
+ if(strlen(SS.license.line2)) {
+ Printf(false, "%Fg %s", SS.license.line2);
+ }
+ Printf(false, "%Fg %s", SS.license.users);
+ } else {
+ Printf(false, "%Fx*** THIS SOFTWARE IS NOT LICENSED ***");
+ Printf(false, "%Fx eval / non-commercial use only");
+ Printf(false, "%Fx buy at %Fl%f%Llhttp://www.mechsketch.com/%E",
+ &ScreenGoToWebsite);
+ }
}
diff --git a/textwin.cpp b/textwin.cpp
index a7676ba9..9db08fcb 100644
--- a/textwin.cpp
+++ b/textwin.cpp
@@ -11,6 +11,7 @@ const TextWindow::Color TextWindow::fgColors[] = {
{ 'r', RGB( 0, 0, 0) },
{ 'x', RGB(255, 20, 20) },
{ 'i', RGB( 0, 255, 255) },
+ { 'g', RGB(160, 160, 160) },
{ 0, 0 },
};
const TextWindow::Color TextWindow::bgColors[] = {
diff --git a/ui.h b/ui.h
index 9241141c..c76e6100 100644
--- a/ui.h
+++ b/ui.h
@@ -130,6 +130,7 @@ public:
static void ScreenColor(int link, DWORD v);
static void ScreenShowConfiguration(int link, DWORD v);
+ static void ScreenGoToWebsite(int link, DWORD v);
static void ScreenStepDimSteps(int link, DWORD v);
static void ScreenStepDimFinish(int link, DWORD v);
@@ -224,6 +225,10 @@ public:
MNU_TRACE_PT,
MNU_STOP_TRACING,
MNU_STEP_DIM,
+ // Help,
+ MNU_LICENSE,
+ MNU_WEBSITE,
+ MNU_ABOUT,
} MenuId;
typedef void MenuHandler(int id);
typedef struct {
diff --git a/win32/w32main.cpp b/win32/w32main.cpp
index ba36edc0..341cc305 100644
--- a/win32/w32main.cpp
+++ b/win32/w32main.cpp
@@ -1,4 +1,5 @@
#include
+#include
#include
#include
#include
@@ -55,25 +56,50 @@ void dbp(char *str, ...)
va_end(f);
}
-void Error(char *str, ...)
+
+static void DoMessageBox(char *str, va_list f, BOOL error)
{
- va_list f;
- char buf[1024];
- va_start(f, str);
+ char buf[1024*50];
vsprintf(buf, str, f);
- va_end(f);
EnableWindow(GraphicsWnd, FALSE);
EnableWindow(TextWnd, FALSE);
+ int flags;
+ if(error) {
+ flags = MB_OK | MB_ICONERROR;
+ } else {
+ flags = MB_OK | MB_ICONINFORMATION;
+ }
HWND h = GetForegroundWindow();
- MessageBox(h, buf, "SolveSpace Error", MB_OK | MB_ICONERROR);
+ MessageBox(h, buf, "SolveSpace", flags);
EnableWindow(TextWnd, TRUE);
EnableWindow(GraphicsWnd, TRUE);
SetForegroundWindow(GraphicsWnd);
}
+void Error(char *str, ...)
+{
+ va_list f;
+ va_start(f, str);
+ DoMessageBox(str, f, TRUE);
+ va_end(f);
+}
+
+void Message(char *str, ...)
+{
+ va_list f;
+ va_start(f, str);
+ DoMessageBox(str, f, FALSE);
+ va_end(f);
+}
+
+
+void OpenWebsite(char *url) {
+ ShellExecute(GraphicsWnd, "open", url, NULL, NULL, SW_SHOWNORMAL);
+}
+
void ExitNow(void) {
PostQuitMessage(0);
}