Oct 21 2010, 03:40 AM Post #1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Coding like a Rockstar! Posts: 1,468 From: ??? Joined: 28-May 09 | WIP CLEO 4 - Features Guide I've made other topics around this idea, but I wanted to combine all I could find out about CLEO 4's many features and explain them. I've gone through the help file many times, but it misses out things which I have found out since. Since most opcode parameters are forwarded straight to a C-line, I went by what parameters the respective C++ functions took. Text Formatting In the help file, there is a topic "Форматирование текста" (text formatting) which explains how to format text. Most of the contents of this topic are a reference to the C++ functions. If you want an English version, go here. It is specific to printf, but contains most of the relevant information. However, this mostly applies to outputting text, which can sometimes do slightly different things than when you are retrieving information. fscanf is an example (opcode 0ADA). Much further down in the topic - a section something along the lines of "text formatting features in SCM". This part gives info on what available features we have in SCM Coding, which of course, is much less than in C. QUOTE Seemingly outdated and erronous there So I'll correct that: QUOTE It goes on to point out that we don't have a wide variety of data types. We only have short strings (which are useless, in my oppinion), long strings, integers and floats. If the one at the C++ website is still too hard for you to understand, here's one that's more relevant to SCM and explains stuff a little deeper. String Specifiers
String Convention This is specific to taking advantage of the SCM and not C. This is the thing I favour most in CLEO 4. CLEO 4 added many new abilities when it comes to handling strings. In the help documentation, this example sticks out most: SANNY 0ACA: show_text_box 0x859BE8 Will print out "That's a really big step". And it is... Instead of a string we can pass any address and CLEO will read the string straight from it. Here's what you'd find at that address: CODE 54 68 61 74 27 73 20 61 20 72 65 61 6C 6C 79 20 62 69 67 20 73 74 65 70 0A 00 00 00 0A D7 A3 3B ... which in text is... That's a really big step ��� ף; CLEO reads from the passed address to the end of the string (the null-terminator). If we were to use 0x859BF8, we'd output "big step". In a nutshell, we can pass any type of string (short string, long string, short string variable or long string variable) or a pointer to any place (pointer to a variable, pointer to a label or pointer to any other address) and it will display any text characters there (anything not null). Taking Advantage of String Convention First, a list of opcodes that can use string convention:
String Pointer I/O 0AD3, 0AD7, 0ADE, 0AE6, 0AE7 and 0AED are all opcodes that can store strings in memory. This could either be memory already in the .exe or new memory, which we can create via static thread memory or dynamically allocated memory. Thread memory is the same as in CLEO 3: SANNY :thread hex 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 end The label :thread now contains 16 blank bytes which you can use to store strings. The last byte needs to remain as a null-terminator, so you should read and write a maximum of 15 characters. To get the address for this thread memory: SANNY 0AC6: 0@ = label @thread offset We can also allocate new memory. SANNY 0AC8: 0@ = allocate_memory 16 However, I've found that for 0AC8, there is often old (probably unused) data, most likely from reallocation of memory from a game restart... So it might be useful to erase all the data like this: SANNY 0A8C: write_memory 0@ size 16 value 0 virtual_protect 0 However, it's not always necessary. Only really necessary if you are using a method to write strings that doesn't put a trailing \0 on the end of the string... or if you just feel compulsive. Remember, all that is stored in 0@ is an address (even for 0AC8) to a memory location. And be aware that changing the value of 0@ will loose that address from the variable, meaning you can't use it again... and you DO want to eventually use it for 0AC9, to free up memory! This also means you can treat this address like any old integer: SANNY 0085: 1@ = 0@ Now you'll have two pointers to the memory. One stored in 1@ and one stored in 0@, which is helpful if you want to do stuff like this: SANNY 0A8E: 1@ = 0@ + 3 0A8C: write_memory 1@ size 1 value 0 virtual_protect 0 Now we've written 0x0 to the 3rd byte, which essentially makes the string at 0@ 2 characters long. Now onto the good stuff. SANNY 0AD3: 0@ = format "%d + %d = %d" 2 2 4 2 + 2 = 4 in ASCII is written to the memory. SANNY 0AD3: 0@ = format "%X" 11 B is written to the memory. SANNY 2@v = "my string" 0AD3: 0@ = format "%s" 2@v my string is written to the memory. SANNY 0AD3: 0@ = format "%c" 0x41 a is written to the memory. More Advanced... SANNY 2@v = "my string" 0AD3: 0@ = format "%c" 2@ s is written to the memory, because it is the last %character of 2@ (the rest is in 3@ and 4@). This specifier (%c) cannot accept strings. SANNY 2@v = "my string" 0AD3: 0@ = format "%.7s" 2@v my stri is written to the memory. A number after the dot specifies the length of the string. Putting It To Use Here's the good part and what the topics main aim to teach is. Reading and Writing Files There are a few problems when it comes to writing files in CLEO. Writing Newlines One of the first you may encounter is the lack of newline support by Sanny Builder. In most compilers, we could do things like this: SANNY "This is one one line.\r\nThis is on another!" The compiler would replace \r with the hex 0xD and \n with the hex 0xA, which makes a new line in most text editors or anything that views files as plain text. Sanny Builder doesn't do this and just writes it as plain text "\r\n". To make a new line in a file, we could either write the whole line in hex or do something like this: SANNY 0AC8: 0@ = allocate_memory_size 42 0AD3: 0@ = format "This is on one line.%c%cThis is on another!" 0xD 0xA Once %c is encountered, the first extra parameter will be read, which is 0xD (\r). Then on the next %c it does the same thing, get the hex character 0xA (\n) and write it. It isn't as pretty as being able to let the compiler do it, but it's less ugly than writing it all in hex... Personally I don't mind wiriting in hex, though... Writing/Reading Lots of Data This problem applies for CLEO 3... but in CLEO 4 the readfile and writefile opcodes weren't modified to allow pointers. There's an easy way past this, though. Lets say we wanna read all data from a file to an allocated memory location but we don't know how big the file is going to be... SANNY 0A9C: 1@ = file 0@ size 0AC8: 2@ = allocate_memory_size 1@ 0AC7: 3@ = var 3@ offset 0A8F: 3@ = 2@ - 3@ 0016: 3@ /= 4 0A9D: readfile 0@ size 1@ to 3@(3@,1i) // contents of the file is written to 2@ gosub @MakeSomeChange 0A9E: writefile 0@ size 1@ from 3@(3@,1i) This post has been edited by Deji: Nov 2 2010, 03:26 PM Reason for edit: Updated: 2nd November 2010 -------------------- | CLEO 4.3.22 | A?i?a?o?3D | UI SDK | Black Market Mod 1.0.1 | GInput 0.3 | Cheat Keyboard | Tactile Cheat Activation | Stream Ini Extender 0.7 | SuperVars | ScrDebug | Vigilante Justice: San Andreas | | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||