Initial commit

This commit is contained in:
Steven McLeod
2019-05-03 21:29:26 -07:00
parent 8af048cee7
commit 4b383664bb
22 changed files with 1724 additions and 0 deletions
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+26
View File
@@ -0,0 +1,26 @@
//
// Created by Steven on 10-Dec-17.
//
#ifndef BITSET_H
#define BITSET_H
//Reference: https://graphics.stanford.edu/~seander/bithacks.html#ConditionalSetOrClearBitsWithoutBranching
#define EVAL_BOOL(n) ((n)!=0)
#define BITS_SET(val, bits) (val) |= (bits)
#define BITS_RESET(val, bits) (val) &= ~(bits)
#define BITS_FLIP(val, bits) (val) ^= (bits)
#define BITS_TEST(val, bits) ((val) & (bits))
#define BITS_TESTNOT(val, bits) (~(val) & (bits))
#define BITS_SET_BIT(val, bitpos) BITS_SET(val, 1 << (bitpos))
#define BITS_RESET_BIT(val, bitpos) BITS_RESET(val, 1 << (bitpos))
#define BITS_FLIP_BIT(val, bitpos) BITS_FLIP(val, 1 << (bitpos))
#define BITS_TEST_BIT(val, bitpos) EVAL_BOOL(BITS_TEST(val, 1 << (bitpos)))
#define BITS_TESTNOT_BIT(val, bitpos) EVAL_BOOL(!BITS_TESTNOT(val, 1 << (bitpos)))
#define BITS_COND_BIT(val, bitpos, bit) do { if(bit) BITS_SET(val, 1 << (bitpos)); else BITS_RESET(val, 1 << (bitpos)); } while(0)
#endif //BITSET_H
+35
View File
@@ -0,0 +1,35 @@
#ifndef COMMON_H
#define COMMON_H
typedef unsigned short ushort;
typedef unsigned long ulong;
struct PSTRUCT {
Byte len;
Byte str[];
};
#define ABS(x) ((x)>0?(x):-(x))
#define HIWORD(x) (((x) & 0xFFFF0000) >> 16)
#define LOWORD(x) ((x) & 0x0000FFFF)
#define TO_PSTRUCT(x) (* (struct PSTRUCT *) (x))
#define PLEN(x) (TO_PSTRUCT(x).len)
#define PSTR(x) (TO_PSTRUCT(x).str)
#ifndef M_PI
#define M_PI 3.1415926535897932
#endif
#define ITOC(n) ((n) + '0')
#define CTOI(n) ((n) - '0')
#define ARRLEN(a) (sizeof(a)/sizeof(*(a)))
#define L2PT(p,n) do { (p).v = HIWORD(n); (p).h = LOWORD(n); } while(0)
#define PT2L(p) (((long)(p).v << 16) | (long) (p).h)
#define TO_PTL(v,h) (((long)LOWORD(v) << 16) | (long) LOWORD(h))
#endif
+265
View File
@@ -0,0 +1,265 @@
#include "freecell.h"
#include "common.h"
#include <string.h>
#define FC_CEXPAND 2
static void SequenceDeck(Card *deck);
static void AddColumnCard(FCColumn **col, Card card);
static Card GetColumnCard(FCColumn *col);
static Card RemoveColumnCard(FCColumn *col);
static FCColumn *ExpandColumnSize(FCColumn *old, Size ptrSize);
static Boolean AlternatingColours(Card a, Card b);
void FreecellInit(FCState *state) {
memset(state, 0, sizeof(*state));
}
void FreecellStartGame(FCState *state, ushort seed) {
state->moves = 0;
state->seedno = seed;
state->cols = FreecellShuffle(seed, state->cols);
memset(state->store, 0, sizeof(state->store));
memset(state->foundation, 0, sizeof(state->foundation));
}
FCColumn **FreecellShuffle(ushort seed, FCColumn **deal) {
ulong state = seed;
ushort i, j;
Card deck[CARD_QTY];
/* Allocate deal memory */
if(!deal) {
deal = (FCColumn **) NewPtr(FC_COLS * sizeof(FCColumn *));
if(!deal) return NULL;
for(i = 0; i < FC_COLS; ++i) {
deal[i] = (FCColumn *) NewPtr(SIZEOF_FCCOLUMN(FC_DCSIZE));
if(!deal[i]) goto err_inner;
}
}
SequenceDeck(deck);
/* Freecell deal algorithm */
for(i = 0; i < CARD_QTY - 1; ++i) {
ushort cidx;
Card toswap;
state = (state * 214013L + 2531011L) & 0x7FFFFFFFL;
cidx = CARD_QTY - 1 - ((state >> 16) % (CARD_QTY - i));
toswap = deck[cidx];
deck[cidx] = deck[i];
deck[i] = toswap;
}
for(i = 0; i < FC_COLS; ++i) {
for(j = 0; 8*j + i < CARD_QTY; ++j) {
deal[i]->cards[j] = deck[8*j + i];
}
deal[i]->qty = j;
}
return deal;
err_inner:
/* Inner deal array error */
while(i--) {
DisposPtr(deal[i]);
}
DisposPtr(deal);
return NULL;
}
void FreecellDisposeDeal(FCColumn **deal) {
ushort i;
if(deal) {
for(i = 0; i < FC_COLS; ++i) {
DisposPtr(deal[i]);
}
DisposPtr(deal);
}
}
/* Legal move requirements:
1: If to is a LastCard, then val(to) == NULL OR
val(to) - 1 == val(from) and to, from have alternating colours.
2: If to is a storage, then val(to) == CARD_NULL
3: If to is a foundation, then val(to) + 1 == val(from) OR
val(to) == NULL and val(from) == 1 and have the same suit.
*/
Boolean FreecellLegalMove(FCState *state, FCZone from, FCZone to) {
Card cardFrom = GetCardAt(state, from);
Card cardTo = GetCardAt(state, to);
if(CARD_EMPTY(cardFrom))
return false;
switch(to.zone) {
case FCZ_LASTCARD:
return CARD_EMPTY(cardTo)
|| (CARD_GETNUM(cardTo) - 1 == CARD_GETNUM(cardFrom)
&& AlternatingColours(cardTo, cardFrom));
case FCZ_COLUMN:
/* Can refer to empty columns as either */
return CARD_EMPTY(cardTo) && CARD_GETNUM(cardFrom) == 13;
case FCZ_STORAGE:
return CARD_EMPTY(cardTo);
case FCZ_FOUNDATION:
return (CARD_EMPTY(cardTo) && CARD_GETNUM(cardFrom) == 1)
|| (CARD_GETNUM(cardTo) + 1 == CARD_GETNUM(cardFrom)
&& CARD_GETSUIT(cardTo) == CARD_GETSUIT(cardFrom));
}
return false;
}
Boolean FreecellPlayMove(FCState *state, FCZone from, FCZone to) {
if(!FreecellLegalMove(state, from, to))
return false;
FreecellForceMove(state, from, to);
++state->moves;
state->lastMove[0] = from;
state->lastMove[1] = to;
return true;
}
void FreecellForceMove(FCState *state, FCZone from, FCZone to) {
Card moveCard;
switch(from.zone) {
case FCZ_LASTCARD:
moveCard = RemoveColumnCard(state->cols[from.num]);
break;
case FCZ_STORAGE:
moveCard = state->store[from.num];
state->store[from.num] = CARD_NULL;
break;
case FCZ_FOUNDATION:
moveCard = state->foundation[from.num];
if(CARD_GETNUM(moveCard) == 1) {
state->foundation[from.num] = CARD_NULL;
} else {
ushort num = CARD_GETNUM(moveCard) - 1;
CARD_SETNUM(state->foundation[from.num], num);
}
break;
}
switch(to.zone) {
case FCZ_LASTCARD:
AddColumnCard(&state->cols[to.num], moveCard);
break;
case FCZ_STORAGE:
state->store[to.num] = moveCard;
break;
case FCZ_FOUNDATION:
state->foundation[to.num] = moveCard;
break;
}
}
void FreecellUndoMove(FCState *state) {
FCZone tmp;
if(state->lastMove[0].zone == FCZ_NONE)
return;
FreecellForceMove(state, state->lastMove[1], state->lastMove[0]);
--state->moves;
tmp = state->lastMove[0];
state->lastMove[0] = state->lastMove[1];
state->lastMove[1] = tmp;
}
void SequenceDeck(Card *deck) {
ushort i;
deck[0] = TO_CARD(0, 3, 13);
for(i = 1; i < CARD_QTY; ++i) {
if(CARD_GETSUIT(deck[i-1]) == 0)
deck[i] = TO_CARD(0, 3, CARD_GETNUM(deck[i-1]) - 1);
else
deck[i] = deck[i-1] - TO_CARD(0, 1, 0);
}
}
void AddColumnCard(FCColumn **col, Card card) {
FCColumn *workCol = *col;
Size ptrSize, reqSize;
if(workCol->qty >= FC_DCSIZE) {
ptrSize = GetPtrSize(workCol);
reqSize = SIZEOF_FCCOLUMN(workCol->qty+1);
if(reqSize > ptrSize) {
*col = ExpandColumnSize(workCol, ptrSize);
workCol = *col;
}
}
workCol->cards[workCol->qty] = card;
++workCol->qty;
}
static Card GetColumnCard(FCColumn *col) {
if(col->qty == 0)
return CARD_NULL;
return col->cards[col->qty-1];
}
Card RemoveColumnCard(FCColumn *col) {
Card card;
if(col->qty == 0)
return CARD_NULL;
--col->qty;
card = col->cards[col->qty];
col->cards[col->qty] = CARD_NULL;
return card;
}
FCColumn *ExpandColumnSize(FCColumn *old, Size ptrSize) {
FCColumn *new = (FCColumn *) NewPtr(ptrSize + FC_CEXPAND * sizeof(Card));
memcpy(new, old, ptrSize);
return new;
}
Card GetCardAt(FCState *state, FCZone elem) {
switch(elem.zone) {
case FCZ_LASTCARD:
case FCZ_COLUMN:
return GetColumnCard(state->cols[elem.num]);
case FCZ_STORAGE:
return state->store[elem.num];
case FCZ_FOUNDATION:
return state->foundation[elem.num];
}
return CARD_NULL;
}
Boolean AlternatingColours(Card a, Card b) {
if(CARD_EMPTY(a) || CARD_EMPTY(b))
return false;
if(CARD_GETSUIT(a) == C_DIAMOND || CARD_GETSUIT(a) == C_HEART) {
return CARD_GETSUIT(b) == C_CLUB || CARD_GETSUIT(b) == C_SPADE;
}
return CARD_GETSUIT(b) == C_DIAMOND || CARD_GETSUIT(b) == C_HEART;
}
+63
View File
@@ -0,0 +1,63 @@
#ifndef FREECELL_H
#define FREECELL_H
#include "common.h"
#include "gametypes.h"
#define FC_STORES 4
#define FC_COLS 8
#define FC_DCSIZE 13
/* Seed values [1, 32001) */
#define FC_SEEDLO 1
#define FC_SEEDHI 32001
/* GameElem Structs */
enum {
FCZ_NONE=0,
FCZ_LASTCARD,
FCZ_COLUMN,
FCZ_STORAGE,
FCZ_FOUNDATION
};
typedef struct {
Byte zone;
Byte num;
} FCZone;
#define FCZONE_EQ(a, b) (\
((a).zone == (b).zone) \
&& ((a).num == (b).num) \
)
#define SIZEOF_FCCOLUMN(qty) (sizeof(FCColumn) + (qty) * sizeof(Card))
typedef struct {
ushort qty;
Card cards[];
} FCColumn;
typedef struct {
ushort moves;
ushort seedno;
FCColumn **cols; /* Size FC_COLS */
Card store[FC_STORES];
Card foundation[4];
FCZone lastMove[2];
} FCState;
/* Game Setup */
void FreecellInit(FCState *state);
void FreecellStartGame(FCState *state, ushort seed);
FCColumn **FreecellShuffle(ushort seed, FCColumn **deal);
void FreecellDisposeDeal(FCColumn **deal);
/* Game Play */
Card GetCardAt(FCState *state, FCZone elem);
Boolean FreecellLegalMove(FCState *state, FCZone from, FCZone to);
Boolean FreecellPlayMove(FCState *state, FCZone from, FCZone to);
void FreecellForceMove(FCState *state, FCZone from, FCZone to);
void FreecellUndoMove(FCState *state);
#endif /* FREECELL_H */
+245
View File
@@ -0,0 +1,245 @@
#include "gameintf.h"
#include "gamewind.h"
#include "gamewindlow.h"
#include "gamemenu.h"
#include "freecell.h"
/* HandleGameClick, DragActionProc */
static FCZone startHoverElem;
static FCZone lastHoverElem;
static Boolean lastHoverLegal;
static pascal void DragActionProc(void);
static short RndRange(short lower, short upper);
FCClickErr HandleGameClick(FCState *state, Point hitPt) {
FCZone hitElem, dragElem;
Point dragPt;
long theLPoint;
Rect boundsRect = {-3, -3, 3, 3};
Rect cardRect;
RgnHandle cardRgn;
FCClickErr theErr;
hitElem = GetGamePtLoc(state, hitPt, &cardRect);
if(hitElem.zone == FCZ_NONE
|| CARD_EMPTY(GetCardAt(state, hitElem))) {
theErr = FCCE_BADHIT;
goto err;
}
/* Drag has slack to differentiate from double-click */
dragPt = hitPt;
OffsetRect(&boundsRect, dragPt.h, dragPt.v);
while(PtInRect(dragPt, &boundsRect) && StillDown()) {
GetMouse(&dragPt);
}
if(!StillDown()) {
theErr = FCCE_NODRAG;
goto err;
}
/* Rect now starts in a different location */
OffsetRect(&cardRect, dragPt.h - hitPt.h, dragPt.v - hitPt.v);
cardRgn = NewRgn();
OpenRgn();
FrameRoundRect(&cardRect, CARD_XRATIO, CARD_YRATIO);
CloseRgn(cardRgn);
/* Create drag outline */
boundsRect = FrontWindow()->portRect;
startHoverElem = hitElem;
lastHoverElem.zone = FCZ_NONE;
lastHoverLegal = false;
theLPoint = DragGrayRgn(cardRgn, dragPt, &boundsRect,
&boundsRect, noConstraint, &DragActionProc);
if(BAD_PTL(theLPoint)) {
theErr = FCCE_OOBDRAG;
goto err;
}
dragPt.v += HIWORD(theLPoint);
dragPt.h += LOWORD(theLPoint);
dragElem = GetGamePtLoc(state, dragPt, &cardRect);
if(dragElem.zone == FCZ_NONE) {
theErr = FCCE_BADDRAG;
goto err;
}
if(!lastHoverLegal) {
theErr = FCCE_BADMOVE;
goto err;
}
InvertRoundRect(&cardRect, CARD_XRATIO, CARD_YRATIO);
/* Play move */
if(!FreecellPlayMove(state, hitElem, dragElem)) {
theErr = FCCE_BADMOVE;
goto err;
}
GameDrawMove(state, hitElem, dragElem);
return FCCE_OK;
err:
/* In case other deinit needs to occur */
return theErr;
}
pascal void DragActionProc(void) {
Point mousePoint;
FCZone hoverElem;
Rect hoverRect, unhoverRect;
FCState *state;
/* Invert spaces that are able to be placed */
GetMouse(&mousePoint);
state = (FCState *) GetWRefCon(FrontWindow());
hoverElem = GetGamePtLoc(state, mousePoint, &hoverRect);
if(FCZONE_EQ(hoverElem, lastHoverElem))
return;
if(lastHoverLegal) {
GetFCZoneRect(state, lastHoverElem, &unhoverRect);
InvertRoundRect(&unhoverRect, CARD_XRATIO, CARD_YRATIO);
}
lastHoverElem = hoverElem;
lastHoverLegal = FreecellLegalMove(state, startHoverElem, hoverElem);
if(lastHoverLegal) {
InvertRoundRect(&hoverRect, CARD_XRATIO, CARD_YRATIO);
}
}
void GameDrawMove(FCState *state, FCZone from, FCZone to) {
Card replaceCard = GetCardAt(state, from);
Card moveCard = GetCardAt(state, to);
switch(from.zone) {
case FCZ_LASTCARD:
DrawRemovedColCard(state->cols[from.num], from.num);
break;
case FCZ_STORAGE:
DrawStorageCard(replaceCard, from.num);
break;
case FCZ_FOUNDATION:
DrawFoundationCard(replaceCard, from.num);
break;
}
switch(to.zone) {
case FCZ_LASTCARD:
DrawLastColumnCard(state->cols[to.num], to.num);
break;
case FCZ_STORAGE:
DrawStorageCard(moveCard, to.num);
break;
case FCZ_FOUNDATION:
DrawFoundationCard(moveCard, to.num);
break;
}
if(state->moves <= 1) {
MenuUndoState(true);
}
}
void GameNewGame(FCState *state, ushort seed) {
Boolean needTitleUpdate;
if(!seed) {
seed = RndRange(FC_SEEDLO, FC_SEEDHI);
}
needTitleUpdate = seed != state->seedno;
FreecellStartGame(state, seed);
if(needTitleUpdate) {
WindUpdateTitle(FrontWindow());
}
MenuUndoState(false);
ForceRedraw();
}
/* To optimize */
/* elemRect can be NULL if not required */
FCZone GetGamePtLoc(FCState *state, Point pt, Rect *elemRect) {
FCZone res;
Rect boundsRect;
ushort i;
for(i = 0; i < FC_STORES + 4; ++i) {
GetStoreRect(i, &boundsRect);
if(PtInRect(pt, &boundsRect)) {
res.zone = (i >= FC_STORES) ? FCZ_FOUNDATION : FCZ_STORAGE;
res.num = (i >= FC_STORES) ? i - FC_STORES : i;
if(elemRect) *elemRect = boundsRect;
return res;
}
}
for(i = 0; i < FC_COLS; ++i) {
GetColumnRect(i, state->cols[i]->qty, &boundsRect);
if(PtInRect(pt, &boundsRect)) {
if(elemRect) *elemRect = boundsRect;
GetLastStackedRect(i, state->cols[i]->qty, &boundsRect);
if(PtInRect(pt, &boundsRect)) {
res.zone = FCZ_LASTCARD;
if(elemRect) *elemRect = boundsRect;
} else {
res.zone = FCZ_COLUMN;
}
res.num = i;
return res;
}
}
res.zone = FCZ_NONE;
if(elemRect)
SetRect(elemRect, 0, 0, 0, 0);
return res;
}
void GetFCZoneRect(FCState *state, FCZone elem, Rect *elemRect) {
switch(elem.zone) {
case FCZ_LASTCARD: {
FCColumn *theColumn = state->cols[elem.num];
GetLastStackedRect(elem.num, theColumn->qty, elemRect);
} break;
case FCZ_COLUMN: {
FCColumn *theColumn = state->cols[elem.num];
GetColumnRect(elem.num, theColumn->qty, elemRect);
} break;
case FCZ_STORAGE:
GetStoreRect(elem.num, elemRect);
break;
case FCZ_FOUNDATION:
GetStoreRect(elem.num + FC_STORES, elemRect);
break;
default:
SetRect(elemRect, 0, 0, 0, 0);
}
}
/* If upper <= lower then result undefined. */
short RndRange(short lower, short upper) {
asm {
CLR.W -(sp) ; d0 = Random()
_Random
CLR.L d0
MOVE.W (sp)+, d0
MOVE.W upper, d1 ; d1 = upper - lower
SUB.W lower, d1
DIVU.W d1, d0 ; d0 = d0 % d1
SWAP d0
ADD.W lower, d0
}
}
+26
View File
@@ -0,0 +1,26 @@
#ifndef GAMEINTF_H
#define GAMEINTF_H
#include "freecell.h"
#define GAME_RANDOMSEED 0
typedef enum {
FCCE_OK=0,
FCCE_BADHIT,
FCCE_NODRAG,
FCCE_OOBDRAG,
FCCE_BADDRAG,
FCCE_BADMOVE
} FCClickErr;
/* Mouse and Gameplay Handlers */
FCClickErr HandleGameClick(FCState *state, Point hitPt);
void GameDrawMove(FCState *state, FCZone from, FCZone to);
void GameNewGame(FCState *state, ushort seed);
FCZone GetGamePtLoc(FCState *state, Point pt, Rect *elemRect);
void GetFCZoneRect(FCState *state, FCZone elem, Rect *elemRect);
#endif /* GAMEINTF_H */
+131
View File
@@ -0,0 +1,131 @@
#include "gamemenu.h"
#include "common.h"
#include "gamewind.h"
#include "gamestate.h"
#include "gameintf.h"
#define MBAR_ID 128
enum {
appleID=128,
fileID,
editID
};
enum {
apple_aboutID=1
};
enum {
file_newID=1,
file_openID,
file_restartID,
file_quitID=5
};
enum {
edit_undoID=1,
edit_cutID=3,
edit_copyID,
edit_pasteID,
edit_clearID
};
static void DoAppleMenu(short item);
static void DoFileMenu(short item);
static void DoEditMenu(short item);
void MenuCreate(void) {
Handle mHandle;
mHandle = GetNewMBar(MBAR_ID);
SetMenuBar(mHandle);
DrawMenuBar();
mHandle = (Handle) GetMHandle(appleID);
AddResMenu((MenuHandle) mHandle, 'DRVR');
}
void MenuEvent(long menuitem) {
short menuID = HIWORD(menuitem);
short itemID = LOWORD(menuitem);
switch(menuID) {
case appleID: DoAppleMenu(itemID); break;
case fileID: DoFileMenu(itemID); break;
case editID: DoEditMenu(itemID); break;
}
}
void MenuEditState(Boolean active) {
MenuHandle theMenu = GetMHandle(editID);
if(active) {
EnableItem(theMenu, edit_cutID);
EnableItem(theMenu, edit_copyID);
EnableItem(theMenu, edit_pasteID);
EnableItem(theMenu, edit_clearID);
} else {
DisableItem(theMenu, edit_cutID);
DisableItem(theMenu, edit_copyID);
DisableItem(theMenu, edit_pasteID);
DisableItem(theMenu, edit_clearID);
}
}
void MenuUndoState(Boolean active) {
MenuHandle theMenu = GetMHandle(editID);
if(active) {
EnableItem(theMenu, edit_undoID);
} else {
DisableItem(theMenu, edit_undoID);
}
}
void DoAppleMenu(short item) {
GrafPtr *oldPort;
MenuHandle theMenu = GetMHandle(appleID);
if(item > apple_aboutID) {
StringPtr name = (StringPtr) NewPtr(sizeof(Str255));
GetPort(&oldPort);
GetItem(theMenu, item, name);
OpenDeskAcc(name);
SetPort(oldPort);
DisposPtr(name);
} else {
DlogAbout();
}
}
void DoFileMenu(short item) {
MenuHandle theMenu = GetMHandle(fileID);
switch(item) {
case file_newID:
GameNewGame(&gstate.fcGame, GAME_RANDOMSEED);
break;
case file_openID: {
ushort openSeed = DlogOpenGame();
if(openSeed != -1) {
GameNewGame(&gstate.fcGame, openSeed);
}
} break;
case file_restartID:
GameNewGame(&gstate.fcGame, GAME_RANDOMSEED);
break;
case file_quitID:
gstate.running = false;
break;
}
}
void DoEditMenu(short item) {
switch(item) {
case edit_undoID:
FreecellUndoMove(&gstate.fcGame);
GameDrawMove(&gstate.fcGame, gstate.fcGame.lastMove[0],
gstate.fcGame.lastMove[1]);
break;
}
}
+9
View File
@@ -0,0 +1,9 @@
#ifndef GAMEMENU_H
#define GAMEMENU_H
void MenuCreate(void);
void MenuEvent(long menuitem);
void MenuEditState(Boolean active);
void MenuUndoState(Boolean active);
#endif /* GAMEMENU_H */
+13
View File
@@ -0,0 +1,13 @@
#ifndef GAMESTATE_H
#define GAMESTATE_H
#include "freecell.h"
struct GlobalState {
FCState fcGame;
Boolean running;
};
extern struct GlobalState gstate;
#endif /* GAMESTATE_H */
+31
View File
@@ -0,0 +1,31 @@
#ifndef GAMETYPES_H
#define GAMETYPES_H
/* Card Type: ffssnnnn. High order can be used for flags. */
#define CARD_FMASK 0xC0
#define CARD_SMASK 0x30
#define CARD_NMASK 0x0F
#define CARD_GETFLAGS(c) ((Byte) ((c & CARD_FMASK) >> 6))
#define CARD_GETSUIT(c) ((Suit) ((c & CARD_SMASK) >> 4))
#define CARD_GETNUM(c) ((short) (c & CARD_NMASK))
#define CARD_SETFLAGS(c, f) ((c) = ((c & ~CARD_FMASK) | ((f) << 6)))
#define CARD_SETSUIT(c, s) ((c) = ((c & ~CARD_SMASK) | ((s) << 4)))
#define CARD_SETNUM(c, n) ((c) = ((c & ~CARD_NMASK) | (n)))
#define TO_CARD(f, s, n) ((Card) (((f) << 6) | ((s) << 4) | (n)))
#define CARD_NULL ((Card) 0)
#define CARD_EMPTY(c) (CARD_GETNUM(c) == 0)
#define CARD_QTY 52
typedef Byte Card;
typedef enum Suit {
C_CLUB=0,
C_DIAMOND,
C_HEART,
C_SPADE
} Suit;
#endif /* GAMETYPES_H */
+344
View File
@@ -0,0 +1,344 @@
#include "gamewind.h"
#include "gamewindlow.h"
#include "common.h"
#include "pstring.h"
#include "strntol.h"
#include <string.h>
#include <ctype.h>
#define TITLE_PREF "\pFreeCell - #"
#define DLOG_OPEN 128
#define DLOG_OPEN_INPUT 3
#define DLOG_ABOUT 129
typedef struct {
short dlgMaxIndex;
Handle itmHndl;
Rect itmRect;
short itmType;
Byte itmData[];
} DialogItemList;
static pascal Boolean DigitInputFilter(DialogPtr theDialog,
EventRecord *theEvent, short *itemHit);
static pascal Boolean AboutFilter(DialogPtr theDialog,
EventRecord *theEvent, int *itemHit);
WindowPtr WindCreateTestEnv(Rect *bounds, StringPtr title) {
WindowPtr theWind;
Rect windRect;
title = title ? title : "\pTest Area";
theWind = NewWindow(0L, bounds, title, true,
noGrowDocProc, (WindowPtr) -1L, true, 0);
return theWind;
}
WindowPtr WindCreateGame(FCState *state) {
WindowPtr theWind;
Rect boundsRect;
Rect windRect;
StringPtr titleString;
unsigned char gameNumBuf[6];
Pattern bkpat;
/*
titleString = (StringPtr) NewPtr(sizeof(Str255));
if(!titleString) return NULL;
memcpy(titleString, TITLE_PREF, sizeof(TITLE_PREF));
NumToString(state->seedno, gameNumBuf);
strcat_p(titleString, gameNumBuf);*/
SetRect(&windRect, 0, 0, WIND_XLENGTH, WIND_YLENGTH);
boundsRect = screenBits.bounds;
boundsRect.top += 20;
CentreRect(&windRect, &boundsRect);
theWind = NewWindow(0L, &windRect, "\p", true, noGrowDocProc,
(WindowPtr) -1L, true, 0);
/*DisposPtr(titleString);*/
if(theWind) {
SetPort(theWind);
GetIndPattern(&bkpat, sysPatListID, 21);
BackPat(bkpat);
SetWRefCon(theWind, (long) state);
}
return theWind;
}
void WindUpdateTitle(WindowPtr theWind) {
StringPtr titleString;
unsigned char gameNumBuf[6];
FCState *state = (FCState *) GetWRefCon(theWind);
titleString = (StringPtr) NewPtr(sizeof(Str255));
if(!titleString) return;
memcpy(titleString, TITLE_PREF, sizeof(TITLE_PREF));
NumToString(state->seedno, gameNumBuf);
strcat_p(titleString, gameNumBuf);
SetWTitle(theWind, titleString);
DisposPtr(titleString);
}
ushort DlogOpenGame(void) {
DialogPtr theDialog;
Rect dlogRect, boundsRect;
short itemNo;
Handle textboxHandle;
volatile Size textboxHndlSize;
long inputText;
theDialog = GetNewDialog(DLOG_OPEN, NULL, (WindowPtr) -1);
if(!theDialog) return -1;
dlogRect = theDialog->portRect;
boundsRect = screenBits.bounds;
boundsRect.top += 20;
CentreRect(&dlogRect, &boundsRect);
MoveWindow(theDialog, dlogRect.left, dlogRect.top, true);
ShowWindow(theDialog);
do {
do {
ModalDialog(&DigitInputFilter, &itemNo);
if(itemNo == cancel) {
inputText = -1;
goto err;
}
} while(itemNo != ok);
GetDItem(theDialog, DLOG_OPEN_INPUT, &itemNo,
&textboxHandle, &boundsRect);
textboxHndlSize = GetHandleSize(textboxHandle);
/* Ensure whitespace before / after string */
HLock(textboxHandle);
inputText = strntol(*textboxHandle, textboxHndlSize, NULL, 10);
HUnlock(textboxHandle);
if(inputText < 1 || inputText > 32000) {
SysBeep(1);
} else {
break;
}
} while(true);
err:
DisposDialog(theDialog);
return (ushort) inputText;
}
pascal Boolean DigitInputFilter(DialogPtr theDialog, EventRecord *theEvent,
short *itemHit) {
char theChar;
if(theEvent->what == keyDown || theEvent->what == autoKey) {
theChar = theEvent->message & 0xFF;
if(theChar == '\r' || theChar == '\x03') {
*itemHit = ok;
return true;
} else if(!isdigit(theChar) && theChar != '\b') {
SysBeep(1);
theEvent->what = nullEvent;
}
}
return false;
}
void DlogAbout(void) {
DialogPtr theDialog;
Rect dlogRect;
Rect boundsRect;
short itemNo;
theDialog = GetNewDialog(DLOG_ABOUT, NULL, (WindowPtr) -1);
if(!theDialog) return;
dlogRect = theDialog->portRect;
boundsRect = screenBits.bounds;
boundsRect.top += 20;
boundsRect.bottom /= 2;
CentreRect(&dlogRect, &boundsRect);
MoveWindow(theDialog, dlogRect.left, dlogRect.top, true);
ShowWindow(theDialog);
ModalDialog(&AboutFilter, &itemNo);
DisposDialog(theDialog);
}
pascal Boolean AboutFilter(DialogPtr theDialog, EventRecord *theEvent,
int *itemHit) {
int windowCode;
WindowPtr theWindow;
if(theEvent->what != mouseDown)
return false;
windowCode = FindWindow(theEvent->where, &theWindow);
if(windowCode != inContent || theWindow != theDialog)
return false;
return true;
}
void DrawGameInit(void) {
Point drawPoint;
ushort i;
drawPoint.v = CARD_BD_Y;
drawPoint.h = CARD_BD_X;
for(i = 0; i < FC_STORES; ++i) {
DrawEmptyFrame(drawPoint);
drawPoint.h += CARD_ST_X + CARD_XLENGTH;
}
drawPoint.h = WIND_XLENGTH - CARD_BD_X - CARD_XLENGTH;
for(i = 0; i < 4; ++i) {
DrawEmptyFrame(drawPoint);
drawPoint.h -= CARD_ST_X + CARD_XLENGTH;
}
}
void ForceRedraw(void) {
GrafPtr thePort;
GetPort(&thePort);
InvalRect(&thePort->portRect);
}
void DrawAll(FCState *state) {
DrawClear();
DrawStorage(state->store);
DrawFoundation(state->foundation);
DrawPlayfield(state->cols);
}
void DrawClear(void) {
GrafPtr thePort;
Rect *clearRect;
GetPort(&thePort);
clearRect = &thePort->portRect;
EraseRect(clearRect);
}
void DrawPlayfield(FCColumn **cols) {
Point drawPoint;
ushort i;
drawPoint.v = CARD_BD_Y + CARD_YLENGTH + CARD_ST_SEP;
drawPoint.h = CARD_BD_X;
for(i = 0; i < FC_COLS; ++i) {
DrawStack(cols[i]->cards, drawPoint, cols[i]->qty);
drawPoint.h += CARD_XLENGTH + CARD_PF_X;
}
}
void DrawStorage(Card *cards) {
Point drawPoint;
ushort i;
drawPoint.v = CARD_BD_Y;
drawPoint.h = CARD_BD_X;
for(i = 0; i < FC_STORES; ++i) {
if(!CARD_EMPTY(cards[i])) {
DrawCard(cards[i], drawPoint);
} else {
DrawEmptyFrame(drawPoint);
}
drawPoint.h += CARD_ST_X + CARD_XLENGTH;
}
}
void DrawFoundation(Card *cards) {
Point drawPoint;
ushort i;
drawPoint.v = CARD_BD_Y;
drawPoint.h = WIND_XLENGTH - CARD_BD_X - CARD_XLENGTH;
for(i = 0; i < 4; ++i) {
if(!CARD_EMPTY(cards[3-i])) {
DrawCard(cards[3-i], drawPoint);
} else {
DrawEmptyFrame(drawPoint);
}
drawPoint.h -= CARD_ST_X + CARD_XLENGTH;
}
}
void DrawStorageCard(Card card, ushort pos) {
Point drawPoint;
drawPoint = GetStorePt(pos);
if(!CARD_EMPTY(card)) {
DrawCard(card, drawPoint);
} else {
DrawEmptyFrame(drawPoint);
}
}
void DrawFoundationCard(Card card, ushort pos) {
Point drawPoint;
drawPoint = GetStorePt(FC_STORES + pos);
if(!CARD_EMPTY(card)) {
DrawCard(card, drawPoint);
} else {
DrawEmptyFrame(drawPoint);
}
}
void DrawLastColumnCard(FCColumn *col, ushort colno) {
Point drawPoint;
drawPoint = GetColumnPt(colno);
DrawStackedCard(col->cards[col->qty-1], drawPoint, col->qty-1);
}
void DrawRemovedColCard(FCColumn *col, ushort colno) {
Point drawPoint;
drawPoint = GetColumnPt(colno);
DrawStackedCard(CARD_NULL, drawPoint, col->qty);
if(col->qty != 0) {
DrawStackedCard(col->cards[col->qty-1], drawPoint, col->qty-1);
} else {
DrawEmptyFrame(drawPoint);
}
}
void CentreRect(Rect *toCentre, const Rect *bounds) {
short bdx, bdy, cdx, cdy;
bdx = bounds->right - bounds->left;
bdy = bounds->bottom - bounds->top;
cdx = toCentre->right - toCentre->left;
cdy = toCentre->bottom - toCentre->top;
if(cdx > bdx || cdy > bdy) return;
toCentre->top = bounds->top + bdy/2 - cdy/2;
toCentre->left = bounds->left + bdx/2 - cdx/2;
toCentre->bottom = toCentre->top + cdy;
toCentre->right = toCentre->left + cdx;
}
/*
switch(elem.zone) {
case FCZ_LASTCARD: {
FCColumn *theColumn = state->cols[elem.num];
if(CARD_EMPTY(theColumn->cards[theColumn->qty-1]))
goto err;
GetLastStackedRect(elem.num, theColumn->qty, &drawRect);
} break;
case FCZ_STORAGE:
if(CARD_EMPTY(state->store[elem.num]))
goto err;
GetStoreRect(elem.num, &drawRect);
break;
case FCZ_FOUNDATION:
if(CARD_EMPTY(state->foundation[elem.num]))
goto err;
GetStoreRect(elem.num + FC_STORES, &drawRect);
break;
default:
goto err;
}
*/
+33
View File
@@ -0,0 +1,33 @@
#ifndef GAMEWIND_H
#define GAMEWIND_H
#include "common.h"
#include "gametypes.h"
#include "freecell.h"
/* Window / Dialog creation */
WindowPtr WindCreateTestEnv(Rect *bounds, StringPtr title);
WindowPtr WindCreateGame(FCState *state);
void WindUpdateTitle(WindowPtr theWind);
ushort DlogOpenGame(void);
void DlogAbout(void);
/* High-Level Drawing */
void DrawGameInit(void);
void ForceRedraw(void);
void DrawAll(FCState *state);
void DrawClear(void);
void DrawPlayfield(FCColumn **cols);
void DrawStorage(Card *cards);
void DrawFoundation(Card *cards);
void DrawStorageCard(Card card, ushort pos);
void DrawFoundationCard(Card card, ushort pos);
void DrawLastColumnCard(FCColumn *col, ushort colno);
void DrawRemovedColCard(FCColumn *col, ushort colno);
/* Misc */
void CentreRect(Rect *toCentre, const Rect *bounds);
#endif /* GAMEWIND_H */
+156
View File
@@ -0,0 +1,156 @@
#include "gamewindlow.h"
void DrawCard(Card card, Point loc) {
Rect cardRect;
static short lineAscent = -1;
static short lineSpacing = -1;
SetRect(&cardRect, 0, 0, CARD_XLENGTH, CARD_YLENGTH);
OffsetRect(&cardRect, loc.h, loc.v);
EraseRoundRect(&cardRect, CARD_XRATIO, CARD_YRATIO);
if(CARD_EMPTY(card)) return;
TextFont(CARDFONT_ID);
TextSize(9);
if(lineSpacing == -1) {
FontInfo cardFontInfo;
GetFontInfo(&cardFontInfo);
lineAscent = cardFontInfo.ascent;
lineSpacing = cardFontInfo.ascent +
cardFontInfo.descent +
cardFontInfo.leading;
}
FillRoundRect(&cardRect, CARD_XRATIO, CARD_YRATIO, white);
FrameRoundRect(&cardRect, CARD_XRATIO, CARD_YRATIO);
InsetRect(&cardRect, (CARD_XLENGTH * 9) / 40,
(CARD_YLENGTH * 9) / 40);
FillRoundRect(&cardRect, CARD_XRATIO, CARD_YRATIO, gray);
loc.h += (CARD_XLENGTH * 2) / 40;
loc.v += (CARD_YLENGTH * 2) / 40 + lineAscent;
MoveTo(loc.h, loc.v);
DrawChar(ITOC(CARD_GETNUM(card)));
/*MoveTo(loc.h, loc.v + lineSpacing);*/
DrawChar(SUIT2CHAR(CARD_GETSUIT(card)));
}
void DrawStack(Card cards[], Point loc, ushort qty) {
ushort i;
if(qty == 0) {
DrawEmptyFrame(loc);
return;
}
for(i = 0; i < qty; ++i) {
DrawCard(cards[i], loc);
loc.v += CARD_PF_Y;
}
}
void DrawStackedCard(Card card, Point loc, ushort pos) {
loc.v += CARD_PF_Y * pos;
DrawCard(card, loc);
}
void DrawEmptyFrame(Point loc) {
PenState ps;
Rect frameRect = {0, 0, CARD_YLENGTH, CARD_XLENGTH};
GetPenState(&ps);
PenPat(black);
PenSize(1,1);
OffsetRect(&frameRect, loc.h, loc.v);
FillRoundRect(&frameRect, CARD_XRATIO, CARD_YRATIO, white);
FrameRoundRect(&frameRect, CARD_XRATIO, CARD_YRATIO);
SetPenState(&ps);
}
/* Returns (INVAL_LOC, INVAL_LOC) on error */
Point GetStorePt(ushort store) {
Point res;
if(store >= FC_STORES + 4) {
L2PT(res, INVAL_PTL);
return res;
}
if(store >= FC_STORES) {
/* Set count from 4-7 to 1-4 backwards */
/*store = (4 - 1) - (store - FC_STORES) + 1;*/
store = (FC_STORES + 4) - store;
res.h = WIND_XLENGTH - CARD_BD_X + CARD_ST_X /* Correction Term */
- store * (CARD_XLENGTH + CARD_ST_X);
} else {
res.h = CARD_BD_X + store * (CARD_XLENGTH + CARD_ST_X);
}
res.v = CARD_BD_Y;
return res;
}
Point GetColumnPt(ushort col) {
Point res;
if(col >= FC_COLS) {
L2PT(res, INVAL_PTL);
return res;
}
res.v = CARD_BD_Y + CARD_YLENGTH + CARD_ST_SEP;
res.h = CARD_BD_X + col * (CARD_XLENGTH + CARD_PF_X);
return res;
}
void GetStoreRect(ushort store, Rect *res) {
topLeft(*res) = GetStorePt(store);
if(BAD_PT(topLeft(*res))) {
SetRect(res, 0, 0, 0, 0);
return;
}
res->bottom = res->top + CARD_YLENGTH;
res->right = res->left + CARD_XLENGTH;
}
void GetColumnRect(ushort col, ushort csize, Rect *res) {
topLeft(*res) = GetColumnPt(col);
if(BAD_PT(topLeft(*res))) {
SetRect(res, 0, 0, 0, 0);
return;
}
if(csize == 0) csize = 1;
res->bottom = res->top + (csize - 1) * CARD_PF_Y + CARD_YLENGTH;
res->right = res->left + CARD_XLENGTH;
}
/* 0 treated as 1 so that cards can be played on empty columns */
void GetStackedCardRect(ushort col, ushort csize, ushort pos, Rect *res) {
if(csize == 0) csize = 1;
if(pos >= csize)
goto err;
topLeft(*res) = GetColumnPt(col);
if(BAD_PT(topLeft(*res)))
goto err;
res->top += pos * CARD_PF_Y;
res->right = res->left + CARD_XLENGTH;
/* If card is last in column, use full area. Otherwise whats seen. */
if(pos == csize - 1) {
res->bottom = res->top + CARD_YLENGTH;
} else {
res->bottom = res->top + CARD_PF_Y;
}
return;
err:
SetRect(res, 0, 0, 0, 0);
}
void GetLastStackedRect(ushort col, ushort csize, Rect *res) {
if(csize < 1) csize = 1;
GetStackedCardRect(col, csize, csize-1, res);
}
+77
View File
@@ -0,0 +1,77 @@
#ifndef GAMEWINDLOW_H
#define GAMEWINDLOW_H
#include "common.h"
#include "freecell.h"
/* Game Dimensions */
#define CARDFONT_ID 25
#define CARDFONT_PT 12
#define CARD_XRATIO 5
#define CARD_YRATIO 7
#define CARD_SCALE 9
#define CARD_XLENGTH (CARD_XRATIO * CARD_SCALE)
#define CARD_YLENGTH (CARD_YRATIO * CARD_SCALE)
/*
^
|BD_Y
|
BD_Xv ST_X ST_GAP
<-->+--+<---->+--+ ... <------>
| | | |
| | | |
+--+ +--+
^
ST|
SEP| XLEN
v PF_X <-->
+--+<----->+--+^
PF_Y| | | ||YLEN
+--+ | ||
| | +--+v
...
*/
/* Set to a ratio ? */
#define CARD_PF_X 10
#define CARD_PF_Y 12
#define CARD_ST_X ((WIND_XLENGTH - (2*CARD_BD_X) - (8*CARD_XLENGTH) \
- CARD_ST_GAP) / 6)
#define CARD_ST_GAP 40
#define CARD_ST_SEP (2*CARD_BD_Y)
#define CARD_BD_X 10
#define CARD_BD_Y 10
#define WIND_XLENGTH ((2*CARD_BD_X)+(8*CARD_XLENGTH)+(7*CARD_PF_X))
#define WIND_YLENGTH \
(((long) (screenBits.bounds.bottom-20) * WIND_XLENGTH) / screenBits.bounds.right)
#define SUIT2CHAR(s) ('0' - 1 - (s))
#define INVAL_LOC (0x8000)
#define INVAL_PTL (0x80008000L)
#define BAD_PT(p) (((p).h == INVAL_LOC) && ((p).v == INVAL_LOC))
/*#define BAD_PT(p) BAD_PTL(PT2L(p))*/
#define BAD_PTL(p) (p == INVAL_PTL)
/* Low-Level Drawing */
void DrawCard(Card card, Point loc);
void DrawStackedCard(Card card, Point loc, ushort pos);
void DrawStack(Card cards[], Point loc, ushort qty);
void DrawEmptyFrame(Point loc);
/* Low-Level Point Access */
Point GetStorePt(ushort store);
Point GetColumnPt(ushort col);
void GetStoreRect(ushort store, Rect *res);
void GetColumnRect(ushort col, ushort csize, Rect *res);
void GetStackedCardRect(ushort col, ushort csize, ushort pos, Rect *res);
void GetLastStackedRect(ushort col, ushort csize, Rect *res);
#endif /* GAMEWINDLOW_H */
+144
View File
@@ -0,0 +1,144 @@
/* TODO
- Bug where starting a new game causes some cards to not be drawn properly
and may leave residual cards in foundation / storage
- Optimize DlogOpenGame string to int
*/
#include "gamewind.h"
#include "gameintf.h"
#include "gamemenu.h"
#include "gamestate.h"
#include "freecell.h"
struct GlobalState gstate = {0};
static Rect dragRect;
static void InitMacintosh(void);
static void InitGameState(void);
static void HandleEvent(short eventMask);
static void HandleMouseDown(EventRecord *theEvent);
static void HandleContentClick(Point mousePt);
short RndRange(short lower, short upper);
void InitMacintosh(void) {
MaxApplZone();
InitGraf(&thePort);
InitFonts();
InitWindows();
InitMenus();
TEInit();
InitDialogs(0L);
randSeed = RndSeed;
FlushEvents(everyEvent, 0);
InitCursor();
}
void InitGameState(void) {
FreecellInit(&gstate.fcGame);
gstate.running = true;
}
void HandleEvent(short eventMask) {
int res;
EventRecord theEvent;
HiliteMenu(0);
SystemTask(); /* Handle desk accessories */
if (!GetNextEvent(eventMask, &theEvent)) return;
switch (theEvent.what) {
case mouseDown:
HandleMouseDown(&theEvent);
break;
case keyDown:
if(theEvent.modifiers & cmdKey) {
MenuEvent(MenuKey(theEvent.message & 0xFF));
}
/* FALLTHROUGH */
case autoKey:
break;
case activateEvt:
if(theEvent.modifiers & activeFlag) {
MenuEditState(false);
} else {
MenuEditState(true);
}
break;
case updateEvt:
BeginUpdate((WindowPtr) theEvent.message);
/*EraseRect(&((WindowPtr) theEvent.message)->portRect);*/
DrawAll(&gstate.fcGame);
EndUpdate((WindowPtr) theEvent.message);
break;
}
}
void HandleMouseDown(EventRecord *theEvent) {
WindowPtr theWindow;
short windowCode = FindWindow(theEvent->where, &theWindow);
switch(windowCode) {
case inSysWindow:
SystemClick (theEvent, theWindow);
break;
case inDrag:
DragWindow(theWindow, theEvent->where, &dragRect);
break;
case inMenuBar:
MenuEvent(MenuSelect(theEvent->where));
break;
case inContent:
if(theWindow != FrontWindow()) {
SelectWindow(theWindow);
} else {
GlobalToLocal(&theEvent->where);
switch(HandleGameClick(&gstate.fcGame, theEvent->where)) {
/*case FCCE_BADDRAG:*/
case FCCE_BADMOVE:
SysBeep(1);
break;
default:
;
}
}
break;
case inGoAway:
if(TrackGoAway(theWindow, theEvent->where)) {
HideWindow(theWindow);
gstate.running = false;
}
break;
}
}
int main(void) {
Point cardPoint = {10, 10};
WindowPtr testWind;
InitMacintosh();
InitGameState();
MenuCreate();
SetRect(&dragRect, 4, 24, screenBits.bounds.right-4,
screenBits.bounds.bottom-4);
testWind = WindCreateGame(&gstate.fcGame);
GameNewGame(&gstate.fcGame, GAME_RANDOMSEED);
while(gstate.running) {
HandleEvent(everyEvent);
}
FreecellDisposeDeal(gstate.fcGame.cols);
}
+22
View File
@@ -0,0 +1,22 @@
/* Parameter List and Prototypes disabled so that stack frame not created */
#pragma options(!require_protos)
StringPtr strcat_p(/*StringPtr s1, const StringPtr s2*/) {
asm {
MOVEA.L 4(sp), a0 ; A0 = s1
MOVEA.L 8(sp), a1 ; A1 = s2
CLR.L d0
CLR.L d1
MOVE.B (a0), d0 ; D0 = n(s1)
MOVE.B (a1)+, d1 ; D1 = n(s2)
ADD.B d1, (a0)+ ; Update n(s1)
ADDA.L d0, a0 ; Offset s1
TST.B d1
BRA.S @2
@1 MOVE.B (a1)+, (a0)+
SUBQ.B #1, d1
@2 BNE.S @1
MOVE.L 4(sp), d0
}
}
+6
View File
@@ -0,0 +1,6 @@
#ifndef PSTRING_H
#define PSTRING_H
StringPtr strcat_p(StringPtr a, const StringPtr b);
#endif /* PSTRING_H */
+90
View File
@@ -0,0 +1,90 @@
/*
* Adapted from GCC strtol.c
*/
#include "strntol.h"
#include <limits.h>
#include <ctype.h>
long strntol(const char *nptr, size_t sz, char **endptr, int base) {
const char *s = nptr;
unsigned long acc = 0;
int c;
unsigned long cutoff;
int neg = 0, any = 0, cutlim;
++sz; /* Bounds fix */
/* Skip whitespace, pick up +/- sign, detect prefix. */
do {
c = *s++;
--sz;
} while(isspace(c) && sz > 0);
if(sz == 0) {
goto err;
}
if(c == '-') {
neg = 1;
c = *s++;
sz--;
} else if(c == '+') {
c = *s++;
}
if((base == 0 || base == 16) &&
sz >= 2 && c == '0' && (*s == 'x' || *s == 'X')) {
c = s[1];
s += 2;
sz -= 2;
base = 16;
}
if(base == 0) {
base = c == '0' ? 8 : 10;
}
/* Compute cutoff between legal / illegal numbers */
cutoff = neg ? -(unsigned long) LONG_MIN : LONG_MAX;
cutlim = cutoff % (unsigned long) base;
cutoff /= (unsigned long) base;
for(;; --sz, c = *s++) {
if(sz == 0) {
break;
}
if(isdigit(c)) {
c -= '0';
} else if(isalpha(c)) {
c -= isupper(c) ? 'A' - 10 : 'a' - 10;
} else {
break;
}
if(c >= base) {
break;
}
if(any < 0 || acc > cutoff || (acc == cutoff && c > cutlim)) {
any = -1;
} else {
any = 1;
acc *= base;
acc += c;
}
}
if(any < 0) {
acc = neg ? LONG_MIN : LONG_MAX;
} else if(neg) {
acc = -acc;
}
err:
if(endptr != 0) {
*endptr = (char *) (any ? s - 1 : nptr);
}
return acc;
}
+8
View File
@@ -0,0 +1,8 @@
#ifndef STRNTOL_H
#define STRNTOL_H
#include <stddef.h>
long strntol(const char *nptr, size_t sz, char **endptr, int base);
#endif /* STRNTOL_H */