diff options
author | pixel <pixel> | 2003-12-10 02:59:30 +0000 |
---|---|---|
committer | pixel <pixel> | 2003-12-10 02:59:30 +0000 |
commit | 5695a4b1e91154f8b9bae6d62eb9313b973fc6bd (patch) | |
tree | 071dff4a418b445451954efb04d0c1286b315387 /FAQ-cd.txt | |
parent | 0b266c95523f944e62acf9251eb27450a8d73a04 (diff) |
LUALUALUALUALUALUALUALUALUA
Diffstat (limited to 'FAQ-cd.txt')
-rw-r--r-- | FAQ-cd.txt | 730 |
1 files changed, 718 insertions, 12 deletions
@@ -21,7 +21,7 @@ A: No. Q: Why? Nero's a spreaded software! A: It's a commercial tool. Since I don't use any commercial tool, and that no - free-software generates Nero ISO, it won't be supported. + GPL software generates Nero ISO, it won't be supported. Q: What is this tool/library able to? @@ -33,19 +33,18 @@ A: First, you can read/write sectors from/to an iso file. You can also read Additionnaly, it is able to produce patches (.ppf files) instead modifying the iso file, saving you time when you use the right softwares. Actually, it is quite "oriented" on the MODE_2* formats, since it's - the PSX's formats. + the PSX's formats. And now, it is able to interpret LUA scripts. Q: I've heard CDmage or ECCRegen can also correct the sectors for me. -A: Maybe. Since it only runs on Win32 platforms, I've never tried it. +A: Maybe. Since they only run on Win32 platforms, I've never tried them. Q: So, what is the goal of this software? A: To modify (patch) ISO images. Nothing else. And of course I want it free, - opensource, and working on my prefered operating system, Linux. If somebody - can make it working for windows (and I think this is easy to do) it would - please me. I can't do it right now since I don't really have the opportunity - to build Win32 binaries (apart of cygwin's ones) + opensource, and working on my prefered operating system, Linux. I can + actually compile it for windows, using the mingw32 system (the same you + can find in DevC++) Q: Where does the source code for the CRC/ECC comes from? @@ -57,7 +56,7 @@ A: Originally, it has been taken from cdrdao. Yazoo has given some Q: Do you have the right to do so? A: The software is GPL'ed. I've got the right to give modified versions of it, as long as I don't claim the modificated thing it the original, - and as long as I mantion the original authors in it. + and as long as I mention the original authors in it. Q: What a strange name, 'yazedc' ? @@ -67,12 +66,13 @@ A: I've got my own ideas about the name's origin... The easy solution: Q: So, I can modify your code too, create a new tool, and diffuse it? -A: Yes, as long as you give the full source code, that the new software - is also GPL'ed, and that you mantion me as the original writer of the - software, you can. Read the GPL carefully, it's very interesting. +A: Yes, as long as you give me back your changes if I ask them, that the + new software is also GPL'ed, and that you mantion me as the original + writer of the software, you can. Read the GPL carefully, it's very + interesting and instructing. -Q: What is exactly the format of a CD-Rom? +Q: With which information What is exactly the format of a CD-Rom? A: Firstly, when you have a raw sector, you have to understand its primary form. Secondly, the whole CD has an internal format, called the iso9660. The format of the iso9660 is easy to find on the internet. Here is one first easy link: @@ -251,3 +251,709 @@ int is_valid_BCD(uchar x) {return (((x & 15) < 10) && ((x >> 4) < 10));} called XA-Mode1 and XA-Mode2 or simplier: XA-1 and XA-2. I hope this will help you as it helped me writing this software. + + +Q: What's with all this LUA stuff? +A: LUA is a scripting language. There, in cd-tool, it enables you to create + custom patches, or evoluted "mini softwares". If you want to use it, you + first have to learn how LUA works and how you can program it. Look at the + official web site for more informations about this language: www.lua.org + Please, do not bug me about LUA syntax. The huge load of documentation + about this language should be really enough. + + +Q: What's your LUA API? +A: First of all, my LUA distribution is the 5.0, slightly modified. The basic + LUA compiler is not able to understand hex or octal numbers. I made a patch + of my own to add this support. You'll find this patch at the very end of + this document. + + Now, for the API itself. As basic layer, there are some functions that you + will surely need. Those are "andB", "orB", "xorB", "notB", "shr" and "shl". + The and, or, xor and not are there because there is no binary equivalent of + those functions into native LUA. The two others, shr and shl, are + respectively "shift right" and "shift left". All the functions work + internally using 32 bits unsigned integers. + + There is a little 'debug' function that can help: "hex". Its syntax is: + + hex(number[, format]) + + Where "format" is a string, which is "%02x" by default. This function + simply returns the formatted string from the C call printf(format, number) + So, you can actually mess up with the format, and display something else + than plain hexa. And even cause the C host to crash, if you put an + incorrect format string. + + + You also have a "print" function, which takes a string as arguement. Its + display form is platform independant (a newline is automatically added) + but for this actual version of CD-Tool, will only be displayed on verbose + mode (option -v in the command line) + + + Ho, I almost forgot. CD-Tool opens the following LUA libraries before + running any script: + + base, math, string, table + + Please read the LUA manual about those libraries. (Note that the "print" + function of the base library is not here, and is replaced with mine) + + Let's talk about objects now. My API supports two kind of objects. Objects + created from within LUA, and objects exported from the C++. Usually, the + objects created by LUA will be destroyable. It means they have a garbage + collector metamethod, and also a ":destroy()" method, to forcabily destroy + the object. The objects exported by the C++ code will be NOT destroyable, + and any try to call the :destroy() method will just fail. + + Now, about files. My API supports three "kind" of files. Input files, + Output files, and Buffers. Each of them have exactly the same API. The + Buffer one has one more feature that I will discuss below. But first, + here are the constructors. + + Input(filename) + Output(filename) + Buffer([seekable]) + + The variable 'filename' is a string, and 'seekable', a boolean. All those + three functions will return what I call a "handle". Several functions of + the API will take those handles as arguments. The variable seekable is + false by default. A non-seekable buffer is a fifo. You won't be able to + seek through it. But it will take less memory than a seekable one. + Output will always create an empty file, erasing any existing one. + + Now, what you can do with handles. Well, here you have all the "methods" + you can call on these handles: + + :read() + + With no arguments, this method will read a string from the file, up to the + end of the line (or the file) and will eventually remove the end of line + marker (LF or CRLF, autodetected) + + :read(size) + + Will read "size" bytes from the handle, will put them in an array, and + return this array. Beware: usually, LUA coders consider arrays starting + from 1. Mine start from 0. This function will return how much bytes were + actually read from the handle. Beware: if it hits the end of the file, it + will return 0, and will automatically close the handle. + + :readU8() :readU16() :readU32() + + Will read a 8, 16 or 32 bits unsigned number, and return it. + Always Little Endian. + + :write(string) + + Will write the string to the handle. Will return nothing. + + :write(size, array) + + Will write the "size" bytes to the handle, taken inside of "array". + Remember: 0-started arrays huh? Will return the number of bytes + actually written on the file. Should not be useful, except for detecting + a "disk full" error. + + :writeU8(byte) :writeU16(word) :writeU32(dword) + + Will write a 8, 16 or 32 bits unsigned number. + Always Little Endian. + + :copyfrom(handle[, size]) + + Will read up to 'size' bytes from the given handle, and put them inside + of the calling handle. If not present, size will be "-1", which means + "up to the end". The 'source' handle will then be closed (since it hit + the end of file) + + :copyto(handle[, size]) + + This is just the countrary of the function above. And to conciliate minds, + I also have the following global function: + + copyhandle(source, destination[, size]) + + which has still the same meanings than above. + + Now for some booleans methods: + + :isclosed() :canread() :canwrite() :canseek() + + Should be self explanatory. + + :tell() + + Will return the actual file position pointer, or if the handle is non + seekable, will just return a counter of the number of bytes read so far. + + :getname() + + Will return a string containing the name of the handle. May be the filename, + if the called object is an Input or Output handle. Otherwise, will just be + the string "Fifo" or "Buffer", depending on the seekability of the Buffer. + + :getsize() + + Will return the handle size in bytes. If it's a Fifo, it will return the + number of remaining bytes. + + :close() + + Will close the file, and flush the remaining datas in caches. + + :flush() + + Will only flush any datas remaining in caches. + + :seek(pos[, wheel]) + + Will seek the file pointer at position "pos". The wheel argument is here to + tell from which point of view "pos" should be seen. By default, wheel is + SEEK_SET, which tells that "pos" is referring from the start of the file. + But you can also have SEEK_CUR, which says that "pos" is referring from the + actual file pointer. And SEEK_END, which will then make "pos" to be seen + as a relative position from the end of the file. Thus, having a negative + value for "pos" is completely legal. It will return the actual file pointer. + + :setz([level]) + + Will enable the zlib against the handle, at the specified level, from 0 (no + compression) to 9 (max compression). The argument 'level' is 9 by default. + Having a level for an Input handle has no meaning. Having :setz() against + a Buffer will simply fail. When a handle is :setz(), any further read or + write will be transparantly made through the zlib. Thus, if you open an + Output file, and immediately specify a :setz() on it, it will produce a + .gz file, that you can decompress using a lot of well known decompressor + softwares. + + + Now for special features about seekable buffers. First, since they have two + file pointers, the common ":tell()" and ":seek()" will work for the reading + pointer. So I had to add the methods ":wtell()" and ":wseek()" that will + work with the writing pointer. + + Moreover, you can access a seekable buffer as an array, starting from 0. + + That's all for basic files. Let's move out to CD specific objects. Here are + the base "types" we will work with: + + cdutils, direntry, cdfile, cddate, PVD, DirTree, and isobuider. + + Let's discuss them now. A "cdutils" is usually created from "cd-tool", and + passed to the LUA script as the "cdutil" variable. This object holds the + power to read, and eventually write to the iso file, or CD device the user + specified as argument. Of course, if the user specified a device, you will + not be able to write to it. The LUA code can not create a cdutils object. + Coming with the cdutils object are some global variables. These are: + + MODE0 = 0, MODE1 = 1, MODE2 = 2, + MODE2_FORM1 = 3, MODE2_FORM2 = 4, + MODE_RAW = 5, GUESS = 6 + + They should be used each time a "mode" is needed as a sector mode argument. + Many methods of cdutils will ask an optionnal "mode" argument, and will + default to "GUESS" if not specified. + + You can also access these three helpers arrays: + + sec_sizes, an array of sector sizes, depending on the mode + sec_offsts, an array of the actual start point of the sector inside + the raw sector, and + sec_modes, an array of strings that describe the sector modes. + + The least but not the last, it comes with these functions: + + swapword(number) swap endianness of given 16 bits number + swapdword(number) swap endianness of given 32 bits number + + from_bcd(number) convert byte from BCD notation + to_bcd(number) convert byte to BCD notation + is_valid_bcd(number) returns a boolean + + from_msf(msf[, start) + from_msf(m, s, f[, start]) + converts a MSF sector address into a plain sector number. + By default, "start" is 150 (a CD usually starts at 0:02:00, + which is 150 sectors) + + The methods of the cdutils object are: + + :sectorseek(sector) + + Will seek the current reading pointer to the specified sector number. Many + methods of cdutils will ask an optionnal "sector" argument, and will use + the current reading pointer if not specified. + + :guessmode([sector]) + + Will return the guessed mode for the specified sector. + + :readsector([sector[, mode]]) + + Will return an array containing the user datas for the specified sector. + It means that, if "mode" is different of MODE_RAW, that only the user part + of the sector will be returned. + + :readdatas(size[, sector[, mode]]) + + Will return an array containing the user datas for the sectors starting at + "sector", by only reading the user datas specified by the "mode". + + :readfile(handle, size[, sector[, mode]]) + + Will basically do the same, but will write down the datas to the specified + handle. Will return nothing. + + :writesector(array[, sector[, mode]]) + + Will write the array inside the specified sector, using the specified mode. + That is, it will only write inside the user datas of the specified sector. + + :writedatas(array, size[, sector[, mode]]) + + Will write "size" bytes from the array inside the sectors starting at + 'sector', writing only inside the user datas parts. + + :writefile(handle[, size[, sector[, mode]]) + + Will basically do the same, but will read from the specified handle, up + to its end if size equals -1 (default) + + Now, the following methods will return a "direntry" object. I will discuss + them shortly after. + + :findpath(path) :findparent(path) :finddirectory(dir, path) + + For the three functions, the "path" is a string. For the definition of a + path, it can be a directory name, or a filename. It is absolute if it + starts with a "/". Each subdirectory is separated by a "/". Beware: usual + iso9660 filenames ends with the string ";1" + + So, ":findpath" and ":findparent" will look for the directory entry of the + exact path, or the parent containing the path specified in argument. They + will return a "direntry", which is a kind of "dump" of the iso structure + that corresponds to this entry in the file table. The last function, + ":finddirectory()", will look inside of the directory specified as argument + for the relative (unique) filename (that can be either a file, or another + directory). Beware: if the specified direntry points to a file, and not + a directory, it will fail. + + So, what's a "direntry" object ? It's a destroyable read-only object + returned by these three functions, which contains the infos from the + directory entry corresponding to a specific path (a file or a directory) + + You can read a direntry object as an array, which has the following + entries: + + R number - entry size + NExt number - "Number of Extensions" + Sector number - entry sector on CD + Size number - entry size on CD + Year number - date & time + Month number - date & time + Day number - date & time + Hour number - date & time + Minute number - date & time + Second number - date & time + Offset number - date & time + Flags number - entry flags (see an iso9660 doc) + HandleUnit number - should be 0 + HandleGap number - should be 0 + VolSeq number - should be 0 + N number - name size + id string - name + + Moreover, it has the following boolean methods that can query a direntry: + + :ishidden() :isdir() + Will query the Flag entry. There are other entries, but I don't + think they are useful. + + :hasxa() + Will compute if a Sony's XA special entry exists. + + :isxadir() :isxaaudio() :isxastr() :isxaxa() :isxaform1() + If the direntry has an xa entry, this will query it. + + + Now for a quite interesting object. It's the "cdfile" object. It is a + derivate of the "handle" generic object, and thus, will have exactly the + same behavior as an "Input" object. The big thing is that this file comes + from inside the iso file directly, with intelligent sector reading (read: + it will extract correctly STR files) + + So, if you remember all the methods of a handle object, I only have to + tell you how to construct a cdfile object. There are two constructors. + + cdfile(cdutils, direntry[, mode]) + cdfile(cdutils, sector[, size[, mode]]) + + The first one is evident. It will simply clone the given direntry object + into a readable handle. + + The second one is a little bit tricker. You specify a sector number, and + a filesize. But by default, size is -1, which means "autodetect". Yeah, I + know this may sound strange, but, on mode 2 CDs, there are sector flags + that helps delimiting files entries. And since the "tricks" used in games + like Xenogears or Chrono Cross are only directory hiding, those flags were + constructed, and are still here. So, the autodetection will try to find + the correct file size using these flags. Beware, it may fail, and it will + not work at all on mode 1 CDs. + + + Okay, I discussed a lot about iso readings, and small patching. Now for + the real thing: iso reconstruction. + + First, I have to introduce three structures used actively by the functions. + The first is the "cddate" structure. It holds any data necessary to set up + a date for various places in the iso structure. You can read/write the + fields of a cddate. All of them are numbers, and should be + self-explanatory: + + year, month, day, hour, minute, second, hundredths, offset + + The function "cddate" will return an empty destroyable cddate. + + The second structure I have to introduce is the "PVD" structure, which + stands for "Primary Volume Descriptor". It is the basic datas that every + CD has. As the cddate structure, you can access the PVD structure as an + array, which fields are: + + sysid string - system ID + volid string - volume ID + volsetid string - volume set ID + pubid string - publisher ID + prepid string - data preparer ID + appid string - application ID + copyright string - copyright file + abstract string - abstract file + biblio string - bibliography file + volcreat date - volume creation date + modif date - volume modification date + volexp date - volume expiration date + voleff data - effective volume date + + The last field, "appdata", is hidden. It is a 512 bytes array which + contains "application datas", usually zeroes. You can access these bytes + directly by using number as indices of a PVD structure. Starting from 0. + SONY usually puts datas in there, so... + + In order to create a PVD structure, you have several ways. You can create + an empty PDV using the "PVD" function. But there is also three functions + that will clone an existing PVD. Those are: + + createpvd(cdutils) createpvd_handle(handle) createpvd_array(array) + + The cdutils one will automatically look for the sector 16 of the input iso + in order to clone it. The two others will just wait 2048 bytes of data + that contain the user data of a PVD sector. + + + The last useful structure is the "DirTree" structure. It is used for + building the iso directory structure. When finalizing the iso file, these + structures will be dumped as "direntries". So they mostly have the same + fields and meanings. + + LUA code should not construct DirTrees directly. It is possible though, + through the "DirTree" function. Here's the syntax: + + DirTree(father[, isdir]) + + The "father" argument should be the other DirTree which is the parent of + the newer one, or "nil" to create a root directory (*really* not + recommanded imho. I really do not see why you would do that. Apart of + creating a fake rootsystem, which is doable, but not tested though, and + was not designed for it (yet)), The "isdir" argument is a boolean, which + defaults to true. You *have* to set it to false in order to create a plain + file. + + Next, the fields of this objects are: + + isdir boolean - read only attribute + sector number - entry sector index + size number - entry size + hidden boolean - sets the hidden entry flag + hardhide boolean - a trick. see below. + name string - entry name. + creation date - entry creation date + have_xa boolean - has a sony's xa entry + xa_dir boolean - flag "is directory" + xa_audio boolean - flag "is audio track" + xa_str boolean - flag "is str video file" + xa_xa boolean - flag "is xa audio file" + xa_form1 boolean - flag "is in mode 2 form 1" + mode number - direntry sector mode + beware: this "mode" entry is to tell how to store the direntry, + not the file pointing to it. + + father, child, brother are read only entries. They are non destroyable + DirTrees and are used to browse the actual directory tree. + + The "hardhide" attribute is to tell the system to "not dump" the entry + when finalizing the CD. Useful for "hidden" filesystems, like Xenogears. + + And finally, the methods for this object: + + :fromdir(direntry) + + This will copy the directory entry into the called DirTree, except the + sector and size attributes. + + :setbasicxa() + + Will set the basic XA attribute. dir:setbasicxa() is equivalent to the + following piece of code: + + dir.have_xa = true + dir.xa_form1 = true + dir.xa_xa = false + dir.xa_str = false + dir.xa_audio = false + dir.xa_dir = dir.isdir + + + Pfiew, only one object remaining. Here it is. Its name is "isobuilder". + It will control and administrate the creation process of an iso file + by using all of the above structures and functions. + + You can create one from LUA, but CD-Tool might just give one to your + script, with the global name "iso". The constructor has the following + syntax: + + isobuilder(handle[, mode]) + + This will return an isobuilder object, that will write everything out to + the specified file, and will create a 2352-raw iso using the given sector + mode (mode 2 form 1 by default). Actually, it would be nonsense to put + something else than "MODE1" or "MODE2_FORM1" as 'mode' here. + + I have to explain a bit how this isobuilder object works. You will be able + to "append" datas, and retains the sector numbers where the datas were + storen inside the DirTree structures. So, when you're done with putting + your files, the isobuilder object will create all the necessary structures + all around the CD, where it spared some space. + + This object has the following methods: + + :foreword(cdutils) + :foreword_handle(handle[, mode]) + :foreword_array(array[, mode]) + + That will create what I call the "forewords" of the CD, ie the 16 first + sectors. The "cdutils" version will automatically takes the 16 sectors + from the input iso. The two others will read the 16 sectors from the + datas you give it, using the specified mode, which defaults to 2352 bytes + per sector, that is, exactly 37632 bytes. + + Next to it, you have the method + + :setbasics(pvd[, rootsize[, ptsize[, nvd[, rootsect]]]]) + + that will create the first structures, and return the root DirTree of your + new iso. By default, rootsize = 1, ptsize = 1, nvd = 1, and rootsect = -1. + + Please retain the "rootsize" in memory a little bit, we will discuss it + right after. "ptsize" is the size of the path table, in sectors. Note that + there is 3 copies of the path table. So, in reality, this number will be + multiplicated by 4. NVD is the number of volume descriptors. We currently + set the "Primary" volume descriptor, and the end marker, at respectively + sector 16 and 17. So, if you want more volume descriptors, you should set + this variable to another value, and then start to write your custom volume + descriptors at sector 17. Do not forget the end marker, it will always be + put at the sector 17, even if you have several volume descriptors. And + finally, the rootsect is the sector of the rootdirectory, if you want to + place it at a certain sector, and thus, reserving some sectors between + the path table and the rootsector. Note that the default value, -1, just + tell to compute it. + + :createdir(dirtree, name[, size[, direntry[, mode]]]) + + This will create a subdirectory in the specified dirtree object. Size is + defaulted to 1, direntry to nil, and mode to -1. When creating a directory + the isobuilder will hold one or more sectors for it in the iso file. You + can't really tell by advance how much sectors you need however. But since + you are creating patches, you should know how much you actually need. So, + you have to tell how much sectors you reserve for the directory. This is + also true for the root, and thus, the "rootsize" in the :setbasics() + method tells the size of the root directory, in sectors. The direntry + object, when it exists, will help to clone the entry. If "name" is empty, + it will also get cloned. If "size" is 0, it will also get cloned. And + finally, if "mode" is different from -1, the directory will be stored + using this mode. Otherwise, the mode is the default iso mode specified + in the isobuilder object creation. + + :createfile(dirtree, name, file[, direntry[, mode]]) + + This is the companion of the previous method. It will create a file in the + iso, with basically the same behavior. It reads the file from a handle, up + to the end, and append it to the current iso. The argument direntry, if + not nil, will be used to clone the attributes. Even the name if the given + one is "". The file will be immediately stored on disk. The filename will + be automatically be followed by ";1" when dumping, so you don't have to + put it anywhere. + + :close([cuefile[, mode]]) + + Will finalize the isofile, by dumping all the necessary structures. The + argument 'cuefile' is eventually a handle pointing to an output file where + the ".cue" file used for cdrwin will be put, but this feature is not yet + enabled. And mode is there to override the default iso mode, as usual. + + SO, you should only need that. But I also added some methods that you may + want/need/etc... + + :getdispsect() + + This will basically return the iso file size in sectors. + + :putfile(file[, mode[, sector]]) + + This will write a bare file onto the iso. By default, mode is -1 and means + "take the default iso mode", and sector is -1, and means "use the value + returned by :getdispsect()". The function will return the sector number + the file was put on. + + :putdatas(array, size[, mode[, sector]]) + + Basically the same as above, but with an array of bytes rather than a + handle. Beware, still starting at 0. + + :createsector(array[, mode[, sector]]) + + Will write down a sector, by returning the address it was wrote. Array + begining at 0, mode = -1: default mode, sector = -1: use :getdispsect(). + One interesting info to know about this function, is that, when using + mode 2 form X sectors, it will set basic subheaders, especially the flags + "End of Record" and "End of File". So, when all the other functions will + set them, for :createsector, you manually have to specify them using the + following methods: + + :setEOF() + :clearEOF() + + Finally, I wrote one "helper" method, though you can't write it yet using + this API: + + :copydir(dirtree, cdutils, direntry[, mode]) + + This will copy one directory from a cdutils object, inside of the + isobuilder object. The argument 'dirtree' is the destination directory, + 'cdutils' is the source thing, 'direntry' the directory entry from cdutils + to be copied, and mode is here to override the default mode. It will work + recursively. If mode is MODE2_FORM1, and if it encounters a special XA + file stored in plain mode 2, it will compute the necessary datas in order + to correctly copy it. The function doesn't return anything. + + Okay, I presented everything. Now for a quick and simple example of use. + This LUA script, when used with the cd-tool's luapatch command, will act + exactly like the cd-tool's copy command: +
+ dir = cdutil:findpath "/"
+ rsize = dir.Size
+
+ iso:foreword(cdutil)
+ pvd = createpvd(cdutil)
+ root = iso:setbasics(pvd, rsize / 2048)
+ iso:copydir(root, cdutil, cdutil:findpath "/") + + buf = Buffer() + buf:write "Touched!\n" + iso:createfile(root, buf, "TOUCHED.TXT"):setbasicsxa() + + iso:close() + + Pwieew, all done. Good luck ! ;-) + + +Q: What patch did you applied to the LUA compiler? +A: Only one of my own, to add support for hex and octal numbers. Here it is: +diff -u -r1.1 llex.c
+--- src/llex.c 6 Nov 2003 11:56:07 -0000
++++ src/llex.c 19 Nov 2003 23:03:35 -0000
+@@ -172,15 +172,34 @@
+
+ /* LUA_NUMBER */
+ static void read_numeral (LexState *LS, int comma, SemInfo *seminfo) {
++ int oct = 0, hex = 0;
+ size_t l = 0;
+ checkbuffer(LS, l);
+ if (comma) save(LS, '.', l);
+- while (isdigit(LS->current)) {
++ else if (LS->current == '0') {
++ oct = 1;
++ checkbuffer(LS, 1);
++ save_and_next(LS, l);
++ if (LS->current == 'x') {
++ oct = 0;
++ hex = 1;
++ checkbuffer(LS, 1);
++ save_and_next(LS, l);
++ }
++ }
++ while (isdigit(LS->current) || (hex && isxdigit(LS->current))) {
+ checkbuffer(LS, l);
+ save_and_next(LS, l);
+ }
++ checkbuffer(LS, 1);
+ if (LS->current == '.') {
+ save_and_next(LS, l);
++ if (hex || oct) {
++ save(LS, '\0', l);
++ luaX_lexerror(LS,
++ "error in number, mixing decimal point with octal or hexadecimal",
++ TK_NUMBER);
++ }
+ if (LS->current == '.') {
+ save_and_next(LS, l);
+ save(LS, '\0', l);
+@@ -195,6 +214,12 @@
+ }
+ if (LS->current == 'e' || LS->current == 'E') {
+ save_and_next(LS, l); /* read `E' */
++ if (hex || oct) {
++ save(LS, '\0', l);
++ luaX_lexerror(LS,
++ "error in number, mixing exponential with octal or hexadecimal",
++ TK_NUMBER);
++ }
+ if (LS->current == '+' || LS->current == '-')
+ save_and_next(LS, l); /* optional exponent sign */
+ while (isdigit(LS->current)) {
+diff -u -r1.1 lobject.c
+--- src/lobject.c 6 Nov 2003 11:56:07 -0000
++++ src/lobject.c 19 Nov 2003 23:03:35 -0000
+@@ -20,13 +20,6 @@
+ #include "lstring.h"
+ #include "lvm.h"
+
+-
+-/* function to convert a string to a lua_Number */
+-#ifndef lua_str2number
+-#define lua_str2number(s,p) strtod((s), (p))
+-#endif
+-
+-
+ const TObject luaO_nilobject = {LUA_TNIL, {NULL}};
+
+
+@@ -91,7 +84,17 @@
+
+ int luaO_str2d (const char *s, lua_Number *result) {
+ char *endptr;
+- lua_Number res = lua_str2number(s, &endptr);
++ size_t l = strlen(s);
++ lua_Number res;
++ if ((l > 0) && (s[0] == '0')) {
++ if ((l > 2) && (s[1] == 'x')) {
++ res = strtol(s + 2, &endptr, 16);
++ } else {
++ res = strtol(s + 1, &endptr, 8);
++ }
++ } else {
++ res = strtod(s, &endptr);
++ }
+ if (endptr == s) return 0; /* no conversion */
+ while (isspace((unsigned char)(*endptr))) endptr++;
+ if (*endptr != '\0') return 0; /* invalid trailing characters? */
|