Browse Source

owo

master
Zezombye 4 months ago
commit
5b33c5a3d0
10 changed files with 1508 additions and 0 deletions
  1. 27
    0
      AndroidManifest.xml
  2. BIN
      CADDIT.g1a
  3. 68
    0
      Casio-Android_communication_protocol.txt
  4. 548
    0
      MainActivity.java
  5. 86
    0
      activity_main.xml
  6. BIN
      app-release.apk
  7. 341
    0
      fonts.c
  8. 13
    0
      fonts.h
  9. 400
    0
      main.c
  10. 25
    0
      serial.src

+ 27
- 0
AndroidManifest.xml View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.kaanaxinc.portalstudio" >

<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission android:name="android.permission.INTERNET"/>

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:screenOrientation="portrait"
android:configChanges="orientation|keyboardHidden">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>

BIN
CADDIT.g1a View File


+ 68
- 0
Casio-Android_communication_protocol.txt View File

@@ -0,0 +1,68 @@
To exchange the information, the addin and the application must respect the current format.

######### Addin -> Application #########

All the commands are in the form xx; with x being a character.
This is because of a weird bug within the addin that, for some reason, messes up the bits
after the first 3-4 characters. Therefore 3 characters long commands must be the maximum.

Here are the different commands. The first character represents the command,
the second character 'X' represents the argument.

Subreddit:

To connect to a specific subreddit, the command is sX;
Since only one character is allowed the different subs are hardcoded into the addin.
't' = /r/TalesFromTechSupport
'r' = /r/TalesFromRetail
'n' = /r/NoSleep
'c' = /r/Caddit
'a' = /r/AskReddit
'v' = viedemerde.fr (to be implemented)

Post:

Once the user has selected a post, the addin sends the rank of the post, which is interpreted
by the application as its ID.
The command is pX; and the rank of the post is in the range A-Y (25 posts is the maximum).

More characters:

Due to the reception buffer limit of 1 ko, if the application sends that the transmission is not yet finished,
the addin sends ak; once it has finished storing the characters sent.

Next page:

If the application sends that there is more, the user can press a button to send np; which will prompt the
application to send the next 5000 o. The 5000 o limit is because of the RAM limit; there are long posts and the
addin may not be able to stock all the data, so it is transferred with a 5000 o max.

######### Application -> Addin #########

The data read by the addin must be in some kind of weird custom xml format.

################
NOTE: ALL communications MUST be ended with a quote ("), to inform the addin that the transmission is finished.
Therefore you must replace all quotes (whether backslashed or not) with the 0x81 character.
If you still have data to send (reached buffer limit) end your communication with a '<' and then a quote.
If you have reached the 5000 o limit but still have data to send, end it with a ';' then a quote.
################

Since < and > are special characters, replace them with 0x7F and 0x80 respectively.
Don't replace them with &gt; and &lt;, they will be displayed as "&gt;" and "&lt;".

Data for posts:

<r subreddit> <p content of post> <p content of second post> ...

You can customise the subreddit attribute and the contents, you can put whatever you want as long as it's not
a quote or a '>'. Example:

<r /r/AskReddit> <p People of Reddit, where are you banned from and why?\n/u/ArtyStickyGuy 3525 upvotes /r/askreddit>

Data for comments:

<t Text+title of post> <1 first level comment> <2 second level comment> <3 third level comment> <1 another first lvl comment>

Basically the post data must be in the <t> tag, and the comments must be in a numbered tag from 1 to 9.


+ 548
- 0
MainActivity.java View File

@@ -0,0 +1,548 @@
package com.kaanaxinc.portalstudio;

import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.ParcelUuid;
import android.os.StrictMode;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnKeyListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.TextView;
import android.widget.Toast;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Set;

public class MainActivity extends Activity {
// La chaîne de caractères par défaut
private final String defaut = "Le code d'accès est inscrit sur la notice.";
Toast myToast;
Button sendPost = null;
Button sendComment = null;
ImageButton logo = null;
TextView entrezCode = null;
TextView codeSurNotice = null;
TextView texteRecu = null;
Handler bluetoothIn;
boolean currentView = false;
final int handlerState = 0;
String messageFinal = "jmesoiejsmoi";

BluetoothAdapter blueAdapter = BluetoothAdapter.getDefaultAdapter();
private OutputStream outputStream;
private InputStream inStream;
ArrayList<String> postIds = new ArrayList<String>();
int postNumber = 0;
String subreddit = "";

String[] subreddits = {"talesfromtechsupport", "talesfromretail", "caddit", "askreddit", "nosleep"};
char[] subCommands = {'t', 'r', 'c', 'a', 'n'};
String messageAEnvoyer = "";
int progressionEnvoi = 0;
int progressionPage = 0;

String connectionError = "PortalStudio n'a pas pu se connecter au portail. Vérifiez votre connexion bluetooth. Tapez sur le logo pour réessayer la connexion.";

//Initializes the Bluetooth connection.
//Important detail: it always connects to the device that is the lowest in your paired devices list.
private void init() throws IOException {
Thread connexionThread = new Thread(new Runnable()
{
public void run() {
try {
if (blueAdapter != null) {
if (blueAdapter.isEnabled()) {
Set<BluetoothDevice> bondedDevices = blueAdapter.getBondedDevices();

if(bondedDevices.size() > 0){
BluetoothDevice bt2 = null;
for(BluetoothDevice bt : bondedDevices) {
bt2 = bt;
}


BluetoothDevice device = bt2;
ParcelUuid[] uuids = device.getUuids();
BluetoothSocket socket = device.createRfcommSocketToServiceRecord(uuids[0].getUuid());
socket.connect();
outputStream = socket.getOutputStream();
inStream = socket.getInputStream();
}

Log.e("error", "No appropriate paired devices.");
} else {
Log.e("error", "Bluetooth is disabled.");
}
}
} catch (IOException e) {

}
}
});

connexionThread.start();

}

//Writes a string to the bluetooth device.
public void write(String s) throws IOException, NullPointerException {
outputStream.write(s.getBytes());
}

//Once this method is called, begins listening for data.
//Note: always place this method in a if checking if inStream != null. Else it will crash.
void beginListenForData() {
Thread workerThread = new Thread(new Runnable()
{
public void run() {

byte[] buffer = new byte[256];
int bytes;

while(!Thread.currentThread().isInterrupted()) {
try {
bytes = inStream.read(buffer); //read bytes from input buffer
String readMessage = new String(buffer, 0, bytes);
// Send the obtained bytes to the UI Activity via handler
bluetoothIn.obtainMessage(handlerState, bytes, -1, readMessage).sendToTarget();
} catch (IOException e) {
break;
} catch (NullPointerException e) {
toast("Null pointer exception 3");
break;
}
}
}
});

workerThread.start();
}

//Checks if the bluetooth is enabled, and tries connecting.
void checkBluetooth() {
//Prompt user to turn on Bluetooth if Bluetooth is disabled
if (!blueAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, 1);
}

try {
init();
} catch (IOException e2) {

}
}

void toast(String str) {
myToast.setText(str);
myToast.show();
}

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myToast = Toast.makeText(getBaseContext(), "", Toast.LENGTH_SHORT);
sendPost = (Button)findViewById(R.id.sendPost);
sendComment = (Button)findViewById(R.id.sendComment);
codeSurNotice = (TextView)findViewById(R.id.codeSurNotice);
entrezCode = (TextView)findViewById(R.id.entrezCode);
texteRecu = (TextView)findViewById(R.id.texteRecu);
logo = (ImageButton)findViewById(R.id.logo);
logo.setOnClickListener(logoListener);
sendPost.setOnClickListener(sendPostListener);
sendComment.setOnClickListener(sendCommentListener);

//This code allows connecting to the internet without doing so in a new thread.
if (android.os.Build.VERSION.SDK_INT > 9) {
StrictMode.ThreadPolicy policy =
new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
}

//Handler that receives the data from the bluetooth device.
//Any manipulation of the received data must be done here (after the messageFinal assignment to msg.obj).
bluetoothIn = new Handler() {
public void handleMessage(android.os.Message msg) {
if (msg.what == handlerState) {
toast(messageFinal);
messageFinal += (String) msg.obj; //for some reason removing the + causes it to remove the first character
texteRecu.setText(messageFinal);
if (messageFinal.matches("[\\s\\S]*\\w\\w;[\\s\\S]*")) {
char command = messageFinal.charAt(messageFinal.indexOf(";")-2);
char arg = messageFinal.charAt(messageFinal.indexOf(";")-1);
toast("command = " + command + "\narg = " + arg);
if (command == 's') {
for (int i = 0; i < subCommands.length; i++) {
if (arg == subCommands[i]) {
subreddit = subreddits[i];
}
}
getPost("https://reddit.com/r/"+subreddit+".json");

}
else if (command == 'p') {
postNumber = arg-65;
try {
getComments("https://reddit.com/" + postIds.get(postNumber) + ".json");
toast("Post ID: "+ postIds.get(postNumber));
} catch (ArrayIndexOutOfBoundsException e) {
toast("Couldn't find a valid post rank, defaulting to the first post.");
getComments("https://reddit.com/" + postIds.get(0) + ".json");
}
}
else if (command == 'a' && arg == 'k') {
sendString();
}
else if (command == 'n' && arg == 'p') {
/*if (messageAEnvoyer.substring(progressionPage).length() > 5000) {
progressionPage += 5000;
}*/
progressionEnvoi = 0;
sendString();
}
}
}
}
};

}

@Override
public void onResume() {
super.onResume();
// connection methods are best here in case program goes into the background etc
checkBluetooth();
}



//Tapping on the logo retries the connection if it failed.
private OnClickListener logoListener = new OnClickListener() {
@Override
public void onClick(View v) {
codeSurNotice.setText("Connexion en cours...");
try {
init();
} catch (IOException e2) {

}
codeSurNotice.setText(defaut);
}
};

//Listener for the "send post" button. Is debug and planned to be removed.
private OnClickListener sendPostListener = new OnClickListener() {
@Override
public void onClick(View v) {
messageFinal = "";
if (inStream != null) {
beginListenForData();

getPost("https://reddit.com/r/" + subreddit + ".json");
sendString();

} else {
toast(connectionError);
}


}
};

//Listener for the "send comment" button, is also debug.
private OnClickListener sendCommentListener = new OnClickListener() {
@Override
public void onClick(View v) {
currentView = false;
messageFinal = "";
if (inStream != null) {
beginListenForData();

try {
getComments("https://reddit.com/" + postIds.get(postNumber) + ".json");
} catch (ArrayIndexOutOfBoundsException e) {
toast("Index out of bounds!");
}
toast("Post ID: "+ postIds.get(postNumber));
sendString();

} else {
toast(connectionError);
}


}
};

void sendString() {
String aEnvoyer = "";
messageFinal = "";
int progTotale = progressionEnvoi + progressionPage;
//progressionEnvoi = 0;
if (messageAEnvoyer.substring(progressionPage).length() > 1000 && progressionEnvoi < 4000
&& messageAEnvoyer.substring(progTotale, progTotale+1000).length() > 1000) {
aEnvoyer = messageAEnvoyer.substring(
progTotale, progTotale + 1000) + ",\"";

progressionEnvoi += 1000;
} else if (messageAEnvoyer.length() > progressionPage+5000){
int max = messageAEnvoyer.lastIndexOf(messageAEnvoyer.substring(progTotale,progTotale+1000),'>');
boolean mettreChevron = false;
if (max == -1) { //no occurrence of '>'
max = messageAEnvoyer.lastIndexOf(messageAEnvoyer.substring(progTotale,progTotale+1000),'\n');
if (max == -1) //no occurrence of '\n'
max = progressionEnvoi+progressionPage+1000;
mettreChevron = true; //there's no occurrence of '>' so we add one
}

aEnvoyer = messageAEnvoyer.substring(progressionEnvoi+progressionPage, max);
if (mettreChevron) {
aEnvoyer += '>';
//inserts a '<' after
messageAEnvoyer = messageAEnvoyer.substring(0, progTotale+max) + '<' + messageAEnvoyer.substring(progTotale+max+1);
}
aEnvoyer += ";\"";
progressionPage += messageAEnvoyer.lastIndexOf('>', progressionEnvoi+progressionPage);
} else {
aEnvoyer = messageAEnvoyer.substring(progressionEnvoi+progressionPage) + "\"";
}
try {
write(aEnvoyer);
} catch (IOException e) {

} catch (NullPointerException e) {
toast(connectionError);
}
}

//Parses the json from a reddit url containing comments.
//Don't try to understand this, it works, that's all you need to know.
void getComments(String url) {

String str = "test";
try {
str = getText(url);
} catch (Exception e) {
toast("Erreur de connexion");
}
toast(str);
String result = "";
String textPostContent = "";
String commentText = "";
String commentAuthor = "";
String commentScore = "";
String[] jsonAttributes = {"selftext", "title"};
String[] cmtVars = {commentAuthor, commentScore, commentText};
String[] cmtAttributes = {"replies", "author", "score", "body"};

ArrayList<Integer> commentLvls = new ArrayList<Integer>();
int currentComment = -1;
//first, browse through the string
int commentLvl = 1; //that way it's set to 1 after the }}], "after" of the text post

System.out.println("Beginning comment level analysis");
for (int i = 10; i < str.length(); i++) {
if (str.startsWith("}}], \"after\"", i)) {
commentLvl--;
} else if (str.startsWith("[{\"kind\": \"t1\"", i)) {
commentLvl++;
commentLvls.add(commentLvl);

} else if (str.startsWith(" {\"kind\": \"t1\"", i)) {
commentLvls.add(commentLvl);
}

}



for (int i = 0; i < str.length(); i++) {
if (str.startsWith("{\"kind\": \"t3\"", i)) {
int j = 0;
while (!str.substring(i - 1, i + 11).equals("}}], \"after\"")) {
i++;
if (j < jsonAttributes.length && str.startsWith("\"" + jsonAttributes[j] + "\": ", i)) {
i += jsonAttributes[j].length() + 4;
int end = 0;
i++;
end = i;
while (!str.substring(end - 1, end + 3).equals("\", \"")) {
end++;
}
end--;
if (j == 1)
textPostContent = str.substring(i, end) + "\n" + textPostContent;
else
textPostContent += str.substring(i, end);
j++;
}
}
result += "<t " + textPostContent + "> \n";
} else if (str.startsWith("{\"kind\": \"t1\"", i)) {
currentComment++;
int l = i;
int j = 0;
int k = 0;
while (!str.substring(l - 1, l + 11).equals("}}], \"after\"")
&& !str.substring(l - 1, l + 16).equals("}}, {\"kind\": \"t1\"")) {
l++;
if (j < cmtAttributes.length && str.startsWith("\"" + cmtAttributes[j] + "\": ", l)) {
l += cmtAttributes[j].length() + 4;

int end = l;
if (j == 0) {

l += 57;
do {
if (str.startsWith("{\"kind\": \"t1\"", l)) {
k++;
}
if (str.startsWith("}}], \"after\"", l)) {
k--;
}
if (str.startsWith("}}, {\"kind\": \"t1\"", l)) {
l += 10;
}
l++;
} while (k != 0);
l++;
} else {
if (j != 2) {
while (!str.substring(end - 1, end + 3).equals("\", \"")) {
end++;
}
end--;
l++;
} else {
end = str.indexOf(',', l);
}
cmtVars[j - 1] = str.substring(l, end);
}
j++;
}
}
result += "<" + commentLvls.get(currentComment) + " " + cmtVars[2]
+ "\n/u/" + cmtVars[0] + " " + cmtVars[1] + " upvotes>";
}
}
result = modifyText(result);
messageAEnvoyer = result;
progressionEnvoi = 0;
sendString();
}

//Parses the json of a reddit sub (which contains posts)
void getPost(String url) {
String str = "test";
//connects to the given url
try {
str = getText(url);
} catch (Exception e) {
toast("Erreur de connexion");
}

String posts = "";
String currentPostTitle = "";
String currentPostUser = "";
String currentPostId = "";
String currentPostSub = "";
String currentPostUrl = "";
String currentPostScore = "";
String currentPostCmts = "";
String[] postAttributes = {currentPostUrl, currentPostSub, currentPostId, currentPostUser, currentPostScore, currentPostCmts, currentPostTitle};
String[] jsonAttributes = {"domain", "subreddit", "id", "author", "score", "num_comments", "title"};

for (int i = 0; i < str.length(); i++) { //browses through the string
if (str.startsWith("{\"kind\": \"t3\"", i)) { //beginning of post
int j = 0;
while (!str.substring(i - 1, i + 27).equals("}}, {\"kind\": \"t3\", \"data\": {") //loops while post is not finished
&& !str.substring(i - 1, i + 11).equals("}}], \"after\"")) {
i++;
if (j < jsonAttributes.length && str.startsWith("\"" + jsonAttributes[j] + "\": ", i)) { //assigns values of attributes to their variables
i += jsonAttributes[j].length() + 4;
int end = 0;
if (j == 4 || j == 5)
end = str.indexOf(',', i);
else {
i++;
end = i;
while (!str.substring(end - 1, end + 3).equals("\", \"")) {
end++;
}
end--;
}
postAttributes[j] = str.substring(i, end);
j++;
}
}
if (postAttributes[0].startsWith("self."))
postAttributes[0] = "self";
else
postAttributes[0] = "ext.link";

postIds.add(postAttributes[2]);
posts += "<p " + postAttributes[6] + "\n" + postAttributes[0] + " /r/" + postAttributes[1]
+ " /u/" + postAttributes[3] + " " + postAttributes[4] + " upvotes " + postAttributes[5] + " comments>\n";
}

}

messageAEnvoyer = modifyText(posts);
progressionEnvoi = 0;
sendString();
}

//Connects to a given url
public String getText(String url) throws Exception {
URL website = new URL(url);
URLConnection connection = website.openConnection();
connection.setRequestProperty("User-Agent", "android:com.zezombye.caddit:v1.0 (by /u/Zezombye)");
BufferedReader in = new BufferedReader(
new InputStreamReader(
connection.getInputStream()));

StringBuilder response = new StringBuilder();
String inputLine;

while ((inputLine = in.readLine()) != null)
response.append(inputLine);

in.close();
return response.toString();
}

//Replaces special characters in the json
static String modifyText(String str) {
str = str.replaceAll("\\\\\\\"", "&q;"); //replaces \" by &q
str = str.replaceAll("\\n", "\n"); //replaces \n by the LF character
str = str.replaceAll("\\\\\\\\", "\\\\"); //replaces \\ by \
str = str.replaceAll("&amp;", "&");
return str;
}

}

+ 86
- 0
activity_main.xml View File

@@ -0,0 +1,86 @@
<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:background="#C3C3C3">
<ImageButton
android:id="@+id/logo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="40dip"
android:src="@drawable/logo2"
android:background="#00000000"
android:layout_gravity="center"
style="#00000000"
/>
<TextView
android:id="@+id/entrezCode"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Connexion au module bluetooth...\n"
android:textStyle="bold"
android:textColor="#000000"
android:textSize="20sp"
android:gravity="center"
/>
<Button
android:id="@+id/sendPost"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Envoyer le post"
android:gravity="center_horizontal"
/>

<Button
android:id="@+id/sendComment"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Envoyer le commentaire"
android:gravity="center_horizontal"
/>

<TextView
android:id="@+id/codeSurNotice"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Texte reçu :"
android:textColor="#000000"
android:gravity="center"
/>
<TextView
android:id="@+id/texteRecu"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="rien"
android:textColor="#000000"
android:gravity="center"
/>
<!--<EditText
android:id="@+id/codeEntered"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:hint="Entrez le code..."
android:inputType="text"
android:imeOptions="actionSend"
/>
<TextView
android:id="@+id/codeSurNotice"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Le code d'accès est inscrit sur la notice."
android:textColor="#000000"
android:gravity="center"
/>
<ImageButton
android:id="@+id/boutonPortail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="40dip"
android:src="@drawable/fermetureencours"
android:background="#00000000"
style="#00000000"
android:layout_gravity="center"
/>-->
</LinearLayout>

BIN
app-release.apk View File


+ 341
- 0
fonts.c View File

@@ -0,0 +1,341 @@
#include "fonts.h"

/*Here is how a character is defined.

Each character is 7 pixels high. The length is defined by you in the length array.

Because some characters (ex: m, M) are 5 px wide, the total amount is >32 bit,
so the character is stored in an array of 2 longs. The first long is only used if
the character is >32 bit.

For example, the character 'M', of length 3, is the following in binary:

00000
10001
11011
10101
10001
10001
00000

You must not forget the "00000" at the end, else your character will be shifted down by a pixel.
Now just remove the newlines in the character. You should get: 00000100011101110101100011000100000
Put that in a binary to decimal converter and you've got your number representing the character.

Also, make sure to define the correct length, else it will display gibberish.
*/

struct Font normfont = {
{{ //letters
0 //space
},{0, 58 //!
},{0, 184320 //" replaced by 0x81
},{0, 368409920 //#
},{0, 524752832 //$
},{0, 136456 //%
},{0, 6838992 //&
},{0, 48 //'
},{0, 1700 //(
},{0, 2392 //)
},{0, 174592 //*
},{0, 11904 //+
},{0, 3 //
},{0, 3584 //-
},{0, 2 //.
},{0, 38176 ///
},{0, 252792 //0
},{0, 206008 //1
},{0, 237368 //2
},{0, 235128 //3
},{0, 187976 //4
},{0, 249464 //5
},{0, 249720 //6
},{0, 234640 //7
},{0, 253816 //8
},{0, 253560 //9
},{0, 10 //:
},{0, 11 //;
},{0, 43144 //< replaced by 0x7F
},{0, 29120 //=
},{0, 139936 //> replaced by 0x80
},{0, 201744 //?
},{0, 488035776 //@
},{0, 6922128 //A
},{0, 15329760 //B
},{0, 6916448 //C
},{0, 15309280 //D
},{0, 16312560 //E
},{0, 16312448 //F
},{0, 7911776 //G
},{0, 10090896 //H
},{0, 238776 //I
},{0, 7480000 //J
},{0, 10144400 //K
},{0, 8947952 //L
},{0, 599442976 //M
},{0, 10336656 //N
},{0, 6920544 //O
},{0, 15310464 //P
},{0, 6921072 //Q
},{0, 15310480 //R
},{0, 7889376 //S
},{0, 238736 //T
},{0, 10066288 //U
},{0, 588818560 //V
},{0, 588961088 //W
},{0, 185704 //X
},{0, 187024 //Y
},{0, 15878384 //Z
},{0, 3756 //[
},{0, 148552 //backslash
},{0, 3420 //]
},{0, 86016 //^
},{0, 7 //_
},{0, 3648 //`
},{0, 15192 //a
},{0, 158576 //b
},{0, 14616 //c
},{0, 47960 //d
},{0, 15256 //e
},{0, 118048 //f
},{0, 15310 //g
},{0, 158568 //h
},{0, 46 //i
},{0, 1111 //j
},{0, 154984 //k
},{0, 62 //l
},{0, 27973280 //m
},{0, 27496 //n
},{0, 11088 //o
},{0, 27508 //p
},{0, 15193 //q
},{0, 23840 //r
},{0, 924 //s
},{0, 2988 //t
},{0, 23416 //u
},{0, 23376 //v
},{0, 18535744 //w
},{0, 21864 //x
},{0, 23246 //y
},{0, 30008 //z
},{0, 108696 //{
},{0, 62 //|
},{0, 205488 //}
},{0, 448512 //~
},{0, 43144 //< 0x7F
},{0, 139936 //> 0x80
},{0, 184320 //" 0x81
},{0, 50022784 //look of disapproval 0x82
},{0, 496640000 //lenny face eye 0x83
},{0, 138482222 //lenny face nose/mouth 0x84
},{0, 4088 //sunglasses 0x85
},{0, 3840 //*puts on sunglasses* 0x86
},{0, 229376 //overline 0x87
},{0, 693142620 //shrug face 0x88
},{0, 801688 //é 0x89

},{0, 201744 //for some reason, when the dispStr() function seeks the character at 0x90 and above
},{0, 201744 //it actually seeks it 6 characters after. there is absolutely no logical way I can think of
},{0, 201744 //that causes this bug. 0x90 (144) is not a significant number. neither is 6.
},{0, 201744 //so rather than spending 4532 hours debugging, the fix is simple:
},{0, 201744 //just put 6 characters between 0x89 and 0x90.
},{0, 201744

},{0, 1588120 //è 0x90
},{0, 1588056 //à 0x91
},{0, 1596280 //ù 0x92
},{0, 14622 //ç 0x93
},{0, 1617408 //² 0x94
},{0, 55501872 //€ 0x95
},{0, 1048716256 //somme 0x96

}},{ //lengths
3, //space
1, //!
3, //"
5, //#
5, //$
3, //%
4, //&
1, //'
2, //(
2, //)
3, //*
3, //+
1, //,
3, //-
1, //.
3, ///
3, //0
3, //1
3, //2
3, //3
3, //4
3, //5
3, //6
3, //7
3, //8
3, //9
1, //:
1, //;
3, //<
3, //=
3, //>
3, //?
5, //@
4, //A
4, //B
4, //C
4, //D
4, //E
4, //F
4, //G
4, //H
3, //I
4, //J
4, //K
4, //L
5, //M
4, //N
4, //O
4, //P
4, //Q
4, //R
4, //S
3, //T
4, //U
5, //V
5, //W
3, //X
3, //Y
4, //Z
2, //[
3, //backslash
2, //]
3, //^
3, //_
2, //`
3, //a
3, //b
3, //c
3, //d
3, //e
3, //f
3, //g
3, //h
1, //i
2, //j
3, //k
1, //l
5, //m
3, //n
3, //o
3, //p
3, //q
3, //r
2, //s
2, //t
3, //u
3, //v
5, //w
3, //x
3, //y
3, //z
3, //{
1, //|
3, //}
5, //~
3, //<
3, //>
3, //"
5, //look of disapproval
5, //lenny face eye
5, //lenny face nose/mouth
3, //sunglasses
3, //*puts on sunglasses*
3, //overline
5, //shrug face
3, //é
3, //unused lengths, see letters comment
3,
3,
3,
3,
3,
3, //è
3, //à
3, //ù
3, //ç
3, //²
4, //€
5, //somme
3,
3,
3,
3,
3,
3,
3,
3,
3,
3,
3
}
};

//displays a given string, using a given font, at the given coordinates
//returns the height of the string
int dispStr(unsigned char* str, int x2, int y) {
int k = 0;
int x = x2;
int y2 = y;
do { //browses through the given string
//word wrap: if the current character isn't a space, simply display it
if (str[k] != ' ' && str[k] != '\0' && str[k] != '\n') {
if (y >= -6 && y < 68) {
int charlength = normfont.length[str[k]-32];
unsigned long j = 1 << ((7*charlength)%32)-1; //initializes a long for bit checking. The long is equal to 0b10000.. with number of zeroes being the maximum length of the character, minus 1 because there's already a 1.
char i;
for (i = 0; i < 7*charlength; i++) { //browses through the pixels of the character specified, shifting the 1 of j to the right each time, so that it makes 0b01000.., 0b001000... etc
if (normfont.ltr[str[k]-32][1-(7*charlength-i)/33] & j) { //checks if the bit that is a 1 in the j is also a 1 in the character
ML_pixel(x+i%(charlength), y+i/charlength, 1); //if so, locates the pixel at the coordinates, using modulo and division to calculate the coordinates relative to the top left of the character
}
if (j != 1)
j >>= 1;
else
j = 2147483648;
}
}
x += normfont.length[str[k]-32] + 1; //now that the character has been fully displayed, shifts the cursor right by the length of character + 1
} else if (str[k] == '\n') {
y += 8;
x = x2;
} else if (str[k] == ' ') { //the current character is a space, so see if it manages to display the word without going over x=128
int i = x+4; //initializes an int to count the number of total pixels the next word takes
int j = k+1; //initializes the char to the current char+1 (which is always another character)
while (str[j] != 32 && str[j] != '\0' && str[j] != '\n') { //as long as it doesn't encounter another space or end of string
i += normfont.length[str[j]-32]+1; //it increments i by the length of the character + 1
j++;
}
if (i > 128) { //the word can't be displayed, note that it is STRICTLY superior because we added an unnecessary pixel at the end
y += 8; //goes on next line which is 8 pixels down
x = x2; //puts cursor on beginning of line
} else {
x += 4;
}
}
k++;
} while (str[k] != '\0');
return y+8-y2;
}

+ 13
- 0
fonts.h View File

@@ -0,0 +1,13 @@
#ifndef FONTS_H
#define FONTS_H

struct Font {
unsigned long ltr[220][2]; //an array of longs for each character
char length[220]; //array of chars for the length of each character
};

extern struct Font normfont;

int dispStr(unsigned char* str, int x, int y);

#endif

+ 400
- 0
main.c View File

@@ -0,0 +1,400 @@
#include "fxlib.h"
#include <stdio.h>
#include <stdlib.h>
#include "MonochromeLib.h"
#include "fonts.h"
#include "serial.h"

#define SCROLL 8 //number of pixels to scroll at each press of the up/down key

unsigned long posY[2] = {0}; //array holding the scroll for each view, so that if the user comes back from a comment page, he keeps his scroll position on the post page
unsigned int key;
unsigned int postHeights[26] = {0};
//char postComments[26];
char currentView = 0; //0=post, 1=comment

unsigned char strReceived[5010];
char subreddit[3];

unsigned char byteReceived;
unsigned char byteSent;
char stringFinished = 1;



//postComments[0] = strCmt;
//postComments[1] = strCmt2;
//postComments[2] = strCmt3;

void dispPost(unsigned char* str, int strlen);
void getPost(unsigned long posY);
void dispCmt(unsigned char* str, int strlen);
void sendSerial(unsigned char* code);
char getSerial();
void chooseSub();
char dispIntroScreen();

int AddIn_main(int isAppli, unsigned short OptionNum) {
unsigned char serialSettings[]={0,5,0,0,0,0};
//unsigned char test[] = "TestTransmission\n";
Serial_Open(serialSettings);
if (dispIntroScreen())
return 1;
chooseSub();
ML_clear_vram();
//GetKey(&key);
//dispStr(test, normfont, 1, 1, sizeof(test));
while(1) {
ML_clear_vram();
//displays the current view
if (currentView == 0)
dispPost(strReceived, sizeof(strReceived));
else
dispCmt(strReceived, 5010);
//getPost(posY[0]);
ML_pixel(127,31,1); //debug pixel, used to test the "hitbox" of posts
GetKey(&key);
if (key == KEY_CTRL_DOWN || key == KEY_CTRL_UP) {
if (key == KEY_CTRL_DOWN)
posY[currentView] += SCROLL;
else if (posY[currentView] >= SCROLL)
posY[currentView] -= SCROLL;
}
if (key == KEY_CTRL_SHIFT && posY[currentView] >= 64) {
posY[currentView] -= 64;
}
if (key == KEY_CTRL_ALPHA) {
posY[currentView] += 64;
}
if (key == KEY_CTRL_EXE && currentView == 0) {
getPost(posY[0]);
posY[1] = 0;
}
if (key == KEY_CTRL_EXIT && currentView == 1) {
currentView = 0;
sendSerial(subreddit);
getSerial();
}
if (key == KEY_CTRL_VARS && !stringFinished) {
sendSerial("np");
getSerial();
posY[currentView] = 0;
}
if (key == KEY_CTRL_OPTN) {
chooseSub();
}
}

return 1;
}

char dispIntroScreen() {
unsigned char nomAppli[10] = "Caddit";
unsigned char author[15] = "Par Zezombye";
unsigned char pressKey[75] = "Appuyez sur une touche pour commencer la connexion.";
unsigned char credits[100] = "Merci \x91 Leph\x89nixnoir, Dark Storm\n& la communaut\x89 de Plan\x90te Casio!";
unsigned char str[100] = "Connexion en cours...\n\nAppuyez sur [DEL] pour annuler.";
int length = 0;
int l;
int k = 9;
ML_horizontal_line(0, 0, 127, 1);
ML_horizontal_line(17, 0, 127, 1);
for (l = 0; l < k; l++)
length += normfont.length[nomAppli[l]-32] +1;
dispStr(nomAppli, 65-(length/2), 2);
length = 0;
k = 14;
for (l = 0; l < k; l++)
length += normfont.length[author[l]-32] +1;
dispStr(author, 65-(length/2), 9);
dispStr(pressKey, 0, 24);
dispStr(credits, 0, 49);
GetKey(&key);
ML_clear_vram();
dispStr(str, 0, 0);
ML_display_vram();
sendSerial("hi");
if (getSerial())
return 1;
return 0;
}

void chooseSub() {
char str[200] = "Choisissez un subreddit\n\n1: /r/talesfromtechsupport\n2: /r/talesfromretail\n3: /r/askreddit\n4: /r/nosleep\n5: /r/caddit";
char sub;
ML_clear_vram();
dispStr(str, 1, 1);
do {
GetKey(&key);
} while (!(key >= KEY_CHAR_1 && key <= KEY_CHAR_5));
if (key == KEY_CHAR_1)
sub = 't';
else if (key == KEY_CHAR_2)
sub = 'r';
else if (key == KEY_CHAR_3)
sub = 'a';
else if (key == KEY_CHAR_4)
sub = 'n';
else if (key == KEY_CHAR_5)
sub = 'c';
subreddit[0] = 's';
subreddit[1] = sub;
sendSerial(subreddit);
currentView = 0;
getSerial();
}

void sendSerial(unsigned char* code) {
Serial_WriteByte(code[0]);
Serial_WriteByte(code[1]);
Serial_WriteByte(';');
Serial_WriteByte('\n');
}

char getSerial() {
int i = 0;
memset(strReceived, 0, sizeof(strReceived));
Serial_ClearReceiveBuffer();
byteReceived = '\0';
while (1) {
while(Serial_ReadByte(&byteReceived)==0 && byteReceived != '"' && byteReceived != '\0') {
strReceived[i] = byteReceived;
i++;
}
if (byteReceived == '"') {
if (strReceived[i-1] == ',') {
sendSerial("ak");
i--;
strReceived[i] = '\0';
byteReceived = '\0';
Serial_ClearReceiveBuffer();
} else {
if (strReceived[i-1] == ';')
stringFinished = 0;
else
stringFinished = 1;
break;
}
}
if (IsKeyDown(KEY_CTRL_DEL))
return 1;
}
return 0;
//dispStr(strReceived, normfont, 1, 1, i);
//GetKey(&key);
//dispStr(strReceived, normfont, 10, 10, i);
}

void getPost(unsigned long posY) {
int height = 12;
int rankOfPost = 0;
while (1) {
height += postHeights[rankOfPost]+3;
if (height > posY+33) {
unsigned char str[3] = "p ";
str[1] = rankOfPost+65;
ML_clear_vram();
currentView = 1;
sendSerial(str);
getSerial();
break;
}
if (rankOfPost > 25)
break;
rankOfPost++;
}
}

void dispCmt(unsigned char* str, int strlen) {
int i;
int currentPageHeight = 0;
for (i = 0; i < strlen; i++) {
if (str[i] == '<') {
//char test[2] = {0, 0};
//test[0] = str[i+1];
//dispStr("Found a <", normfont, 1, 1, sizeof("Found a <"));
//dispStr(test, normfont, 1, 10, 1);
//GetKey(&key);
if (str[i+1] == 't') { //it is the post
int j = i+3;
int k = 0;
unsigned char post[10000] = {0};
//dispStr("Found text post", normfont, 1, 10, sizeof("Found text post"));
//GetKey(&key);
while (str[j] != '>') { //stocks the text of the post
post[k] = str[j];
k++;
j++;
}
currentPageHeight += dispStr(post, 0, currentPageHeight-posY[1]); //increases the page height and displays it
//ML_horizontal_line(currentPageHeight-posY[1], 0, 127, 1); //draws a line after the post
//currentPageHeight += 1;
i = j;
}
if (str[i+1] >= '1' && str[i+1] <= '9') { //it is a comment
int j = i+3;
int k = 0;
unsigned char comment[2000] = {0};
int heightOfComment = 0;
//dispStr("Found a cmt", normfont, 1, 1, sizeof("Found a cmt"));
//GetKey(&key);
while (str[j] != '>') {
comment[k] = str[j];
k++;
j++;
}
if (str[i+1] == '1') {
ML_horizontal_line(currentPageHeight-posY[1], 0, 127, 1);
//heightOfComment = 2;
currentPageHeight += 3;
}
heightOfComment += dispStr(comment, 2*(str[i+1]-49), currentPageHeight-posY[1]);
for (k = 0; k < 2*(str[i+1]-49); k+=2) {
ML_vertical_line(k, currentPageHeight-2-posY[1], currentPageHeight+heightOfComment-posY[1]-2, 1);
}
//if (k > 2) ML_vertical_line(k, currentPageHeight-posY[1], currentPageHeight+heightOfComment+4-posY[1], 1);
currentPageHeight += heightOfComment;
i = j;
}
}
}
}

//interprets the html and the tags
void dispPost(unsigned char* str, int strlen) {
int i;
int currentPostRank = 0;
int currentPageHeight = 13;
ML_horizontal_line(0-posY[0], 0, 127, 1);
ML_horizontal_line(10-posY[0], 0, 127, 1);
for (i = 0; i < strlen; i++) {
if (str[i] == '<') {
if (str[i+1] == 'p') { //it is a post
int j = i+2;
int heightOfPost = 0;
unsigned char title[512] = {0}; //because f* dynamic allocation, max title size is 300 characters
int k = 0;
int strlen = sizeof(title);
while (str[j] != '>') { //as long as the post tag doesn't end
/*while (str[j] == ' ') //loops through the next attribute
j++;
if (str[j] == 't') { //title attribute*/
//while (str[j] != '"') {
//j += 2;
title[k] = str[j];
k++;
j++;
//}
/*}
if (str[j] == 'a') {
unsigned char attributes[64] = {0};
int k = 0;
int strlen = sizeof(attributes);
int test;
j+=2;
while (str[j] != '"') {
attributes[k] = str[j];
k++;
j++;
}
test = dispStr(attributes, normfont, 0, (currentPageHeight+heightOfPost)-posY[0], k);
heightOfPost += test;
j++;
}
if (str[j] != ' ' && str[j] != 't' && str[j] != 'a' && str[j] != 's') {
break;
}*/
}
heightOfPost += dispStr(title, 0, currentPageHeight-posY[0]);
j++;
postHeights[currentPostRank] = heightOfPost;
ML_horizontal_line(currentPageHeight+postHeights[currentPostRank]-1-posY[0], 0, 127, 1);
currentPageHeight += postHeights[currentPostRank]+3;
currentPostRank++;
i = j;
}
if (str[i+1] == 'r') {
int j = i+3;
int k = 0;
int l;
int subLength = 0;
unsigned char subreddit[64] = {0};
while (str[j] != '>') {
subreddit[k] = str[j];
k++;
j++;
}
for (l = 0; l < k; l++)
subLength += normfont.length[subreddit[l]-32] +1;
dispStr(subreddit, 65-(subLength/2), 3-posY[0]);
}
}
}
}


//****************************************************************************
//************** ****************
//************** Notice! ****************
//************** ****************
//************** Please do not change the following source. ****************
//************** ****************
//****************************************************************************


#pragma section _BR_Size
unsigned long BR_Size;
#pragma section


#pragma section _TOP

//****************************************************************************
// InitializeSystem
//
// param : isAppli : 1 = Application / 0 = eActivity
// OptionNum : Option Number (only eActivity)
//
// retval : 1 = No error / 0 = Error
//
//****************************************************************************
int InitializeSystem(int isAppli, unsigned short OptionNum)
{
return INIT_ADDIN_APPLICATION(isAppli, OptionNum);
}
#pragma section

+ 25
- 0
serial.src View File

@@ -0,0 +1,25 @@
.SECTION P,CODE,ALIGN=4

.MACRO SYSCALL FUNO, SYSCALLNAME, TAIL=nop
.export \SYSCALLNAME'
\SYSCALLNAME'
mov.l #h'\FUNO, r0
mov.l #H'80010070, r2
jmp @r2
\TAIL'
.ENDM

SYSCALL 040C, _Serial_ReadByte
SYSCALL 040D, _Serial_ReadBytes
SYSCALL 040E, _Serial_WriteByte
SYSCALL 040F, _Serial_WriteBytes
SYSCALL 0410, _Serial_WriteByteFIFO
SYSCALL 0411, _Serial_GetRxBufferSize
SYSCALL 0412, _Serial_GetTxBufferFreeCapacity
SYSCALL 0413, _Serial_ClearReceiveBuffer
SYSCALL 0414, _Serial_ClearTransmitBuffer
SYSCALL 0418, _Serial_Open
SYSCALL 0419, _Serial_Close
SYSCALL 0422, _Serial_Peek
SYSCALL 0425, _Serial_IsOpen
.end

Loading…
Cancel
Save