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); }